1 // =================================
  2 // Copyright (c) 2024 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 class DeflateStream : Stream
 26     {
 27         public DeflateStream(Stream* underlyingStream_int compressionLevel_) : this(underlyingStream_compressionLevel_16384)
 28         {
 29         }
 30         public DeflateStream(Stream* underlyingStream_CompressionMode mode_) : this(underlyingStream_mode_16384)
 31         {
 32         }
 33         public DeflateStream(Stream* underlyingStream_int compressionLevel_long bufferSize_) : 
 34             underlyingStream(underlyingStream_)mode(CompressionMode.compress)handle(null)inAvail(0u)bufferSize(bufferSize_)
 35             in(bufferSize)outAvail(0u)outPos(0)outHave(0u)endOfInput(false)endOfStream(false)out(bufferSize)
 36         {
 37             int errorId = 0;
 38             RtmInitZlib(modecompressionLevel_&handleerrorId);
 39             if (errorId != 0)
 40             {
 41                 SetErrorId(errorId);
 42             }
 43         }
 44         public DeflateStream(Stream* underlyingStream_CompressionMode mode_long bufferSize_) : 
 45             underlyingStream(underlyingStream_)mode(mode_)handle(null)inAvail(0u)bufferSize(bufferSize_)
 46             in(bufferSize)outAvail(0u)outPos(0)outHave(0u)endOfInput(false)endOfStream(false)out(bufferSize)
 47         {
 48             int errorId = 0;
 49             RtmInitZlib(modedefaultDeflateCompressionLevel&handleerrorId);
 50             if (errorId != 0)
 51             {
 52                 SetErrorId(errorId);
 53             }
 54         }
 55         suppress DeflateStream(DeflateStream&&);
 56         suppress void operator=(DeflateStream&&);
 57         suppress DeflateStream(const DeflateStream&);
 58         suppress void operator=(const DeflateStream&);
 59         public inline CompressionMode Mode() const
 60         {
 61             return mode;
 62         }
 63         public ~DeflateStream()
 64         {
 65             if (handle != null)
 66             {
 67                 if (mode == CompressionMode.compress)
 68                 {
 69                     Finish();
 70                 }
 71                 RtmDoneZlib(modehandle);
 72             }
 73         }
 74         [nodiscard]
 75         public override Result<int> ReadByte()
 76         {
 77             byte x = 0u;
 78             auto readResult = this->Read(&x1);
 79             if (readResult.Error())
 80             {
 81                 return Result<int>(ErrorId(readResult.GetErrorId()));
 82             }
 83             long bytesRead = readResult.Value();
 84             if (bytesRead == 0)
 85             {
 86                 return Result<int>(-1);
 87             }
 88             return Result<int>(x);
 89         }
 90         [nodiscard]
 91         public override Result<long> Read(byte* buflong count)
 92         {
 93             if (mode != CompressionMode.decompress)
 94             {
 95                 string errorMessage = "Cannot read from DeflateStream in CompressionMode.compress";
 96                 int errorId = AllocateError(errorMessage);
 97                 return Result<long>(ErrorId(errorId));
 98             }
 99             long bytesRead = 0;
100             do
101             {
102                 if (inAvail == 0u && !endOfInput)
103                 {
104                     auto readResult = underlyingStream->Read(cast<byte*>(in.Mem())bufferSize);
105                     if (readResult.Error())
106                     {
107                         return Result<long>(ErrorId(readResult.GetErrorId()));
108                     }
109                     inAvail = cast<uint>(readResult.Value());
110                     if (inAvail == 0u)
111                     {
112                         endOfInput = true;
113                     }
114                     RtmSetInputZlib(in.Mem()inAvailhandle);
115                 }
116                 do
117                 {
118                     if (outHave == 0u && !endOfStream)
119                     {
120                         int errorId = 0;
121                         int ret = RtmInflateZlib(out.Mem()cast<uint>(bufferSize)&outHave&outAvail&inAvailhandleerrorId);
122                         if (errorId != 0)
123                         {
124                             return Result<long>(ErrorId(errorId));
125                         }
126                         if (ret == Z_STREAM_END)
127                         {
128                             endOfStream = true;
129                         }
130                         outPos = 0;
131                     }
132                     while (count > 0 && outHave > 0u)
133                     {
134                         *buf++ = out[outPos++];
135                         --count;
136                         --outHave;
137                         ++bytesRead;
138                     }
139                 }
140                 while (count > 0 && outAvail == 0u);
141             }
142             while (count > 0 && !endOfStream && !endOfInput);
143             if (endOfInput && !endOfStream)
144             {
145                 string errorMessage = "DeflateStream: unexpected end of input";
146                 int errorId = AllocateError(errorMessage);
147                 return Result<long>(ErrorId(errorId));
148             }
149             return Result<long>(bytesRead);
150         }
151         [nodiscard]
152         public override Result<bool> Write(byte x)
153         {
154             return this->Write(&x1);
155         }
156         [nodiscard]
157         public override Result<bool> Write(byte* buflong count)
158         {
159             if (mode != CompressionMode.compress)
160             {
161                 string errorMessage = "Cannot write to DeflateStream in CompressionMode.decompress";
162                 int errorId = AllocateError(errorMessage);
163                 return Result<bool>(ErrorId(errorId));
164             }
165             while (count > 0)
166             {
167                 byte* inP = cast<byte*>(in.Mem());
168                 inAvail = 0u;
169                 while (count > 0 && inAvail < cast<uint>(bufferSize))
170                 {
171                     *inP++ = *buf++;
172                     --count;
173                     ++inAvail;
174                 }
175                 RtmSetInputZlib(in.Mem()inAvailhandle);
176                 do
177                 {
178                     uint have = 0u;
179                     int errorId = 0;
180                     int ret = RtmDeflateZlib(out.Mem()cast<uint>(bufferSize)&have&outAvailhandleZ_NO_FLUSHerrorId);
181                     if (errorId != 0)
182                     {
183                         return Result<bool>(ErrorId(errorId));
184                     }
185                     auto result = underlyingStream->Write(cast<byte*>(out.Mem())cast<long>(have));
186                     if (result.Error())
187                     {
188                         return Result<bool>(ErrorId(result.GetErrorId()));
189                     }
190                 }
191                 while (outAvail == 0u);
192             }
193             return Result<bool>(true);
194         }
195         private Result<bool> Finish()
196         {
197             do
198             {
199                 uint have = 0u;
200                 int errorId = 0;
201                 int ret = RtmDeflateZlib(out.Mem()cast<uint>(bufferSize)&have&outAvailhandleZ_FINISHerrorId);
202                 if (errorId != 0)
203                 {
204                     return Result<bool>(ErrorId(errorId));
205                 }
206                 auto result = underlyingStream->Write(cast<byte*>(out.Mem())cast<long>(have));
207                 if (result.Error())
208                 {
209                     return Result<bool>(ErrorId(result.GetErrorId()));
210                 }
211             }
212             while (outAvail == 0);
213             return Result<bool>(true);
214         }
215         private Stream* underlyingStream;
216         private CompressionMode mode;
217         private long bufferSize;
218         private uint inAvail;
219         private IOBuffer in;
220         private uint outAvail;
221         private long outPos;
222         private uint outHave;
223         private bool endOfInput;
224         private bool endOfStream;
225         private IOBuffer out;
226         private void* handle;
227     }