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