zhao
2021-07-19 8347f2fbddbd25369359dcb2da1233ac48a19fdc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
// BitWriter.cs
// ------------------------------------------------------------------
//
// Copyright (c) 2011 Dino Chiesa.
// All rights reserved.
//
// This code module is part of DotNetZip, a zipfile class library.
//
// ------------------------------------------------------------------
//
// This code is licensed under the Microsoft Public License.
// See the file License.txt for the license details.
// More info on: http://dotnetzip.codeplex.com
//
// ------------------------------------------------------------------
//
// Last Saved: <2011-July-25 18:57:31>
//
// ------------------------------------------------------------------
//
// This module defines the BitWriter class, which writes bits at a time
// to an output stream. It's used by the BZip2Compressor class, and by
// the BZip2OutputStream class and its parallel variant,
// ParallelBZip2OutputStream.
//
// ------------------------------------------------------------------
 
//
// Design notes:
//
// BZip2 employs byte-shredding in its data format - rather than
// aligning all data items in a compressed .bz2 file on byte barriers,
// the BZip2 format uses portions of bytes to represent independent
// pieces of information. This "shredding" starts with the first
// "randomised" bit - just 12 bytes or so into a bz2 file or stream. But
// the approach is used extensively in bzip2 files - sometimes 5 bits
// are used, sometimes 24 or 3 bits, sometimes just 1 bit, and so on.
// It's not possible to send this information directly to a stream in
// this form; Streams in .NET accept byte-oriented input.  Therefore,
// when actually writing a bz2 file, the output data must be organized
// into a byte-aligned format before being written to the output stream.
//
// This BitWriter class provides the byte-shredding necessary for BZip2
// output. Think of this class as an Adapter that enables Bit-oriented
// output to a standard byte-oriented .NET stream. This class writes
// data out to the captive output stream only after the data bits have
// been accumulated and aligned. For example, suppose that during
// operation, the BZip2 compressor emits 5 bits, then 24 bits, then 32
// bits.  When the first 5 bits are sent to the BitWriter, nothing is
// written to the output stream; instead these 5 bits are simply stored
// in the internal accumulator.  When the next 24 bits are written, the
// first 3 bits are gathered with the accumulated bits. The resulting
// 5+3 constitutes an entire byte; the BitWriter then actually writes
// that byte to the output stream. This leaves 21 bits. BitWriter writes
// 2 more whole bytes (16 more bits), in 8-bit chunks, leaving 5 in the
// accumulator. BitWriter then follows the same procedure with the 32
// new bits. And so on.
//
// A quick tour of the implementation:
//
// The accumulator is a uint - so it can accumulate at most 4 bytes of
// information. In practice because of the design of this class, it
// never accumulates more than 3 bytes.
//
// The Flush() method emits all whole bytes available. After calling
// Flush(), there may be between 0-7 bits yet to be emitted into the
// output stream.
//
// FinishAndPad() emits all data, including the last partial byte and
// any necessary padding. In effect, it establishes a byte-alignment
// barrier. To support bzip2, FinishAndPad() should be called only once
// for a bz2 file, after the last bit of data has been written through
// this adapter.  Other binary file formats may use byte-alignment at
// various points within the file, and FinishAndPad() would support that
// scenario.
//
// The internal fn Reset() is used to reset the state of the adapter;
// this class is used by BZip2Compressor, instances of which get re-used
// by multiple distinct threads, for different blocks of data.
//
 
 
using System;
using System.IO;
 
namespace HH.WMS.Utils.Ionic.BZip2
{
 
    internal class BitWriter
    {
        uint accumulator;
        int nAccumulatedBits;
        Stream output;
        int totalBytesWrittenOut;
 
        public BitWriter(Stream s)
        {
            this.output = s;
        }
 
        /// <summary>
        ///   Delivers the remaining bits, left-aligned, in a byte.
        /// </summary>
        /// <remarks>
        ///   <para>
        ///     This is valid only if NumRemainingBits is less than 8;
        ///     in other words it is valid only after a call to Flush().
        ///   </para>
        /// </remarks>
        public byte RemainingBits
        {
            get
            {
                return (byte) (this.accumulator >> (32 - this.nAccumulatedBits) & 0xff);
            }
        }
 
        public int NumRemainingBits
        {
            get
            {
                return this.nAccumulatedBits;
            }
        }
 
        public int TotalBytesWrittenOut
        {
            get
            {
                return this.totalBytesWrittenOut;
            }
        }
 
        /// <summary>
        ///   Reset the BitWriter.
        /// </summary>
        /// <remarks>
        ///   <para>
        ///     This is useful when the BitWriter writes into a MemoryStream, and
        ///     is used by a BZip2Compressor, which itself is re-used for multiple
        ///     distinct data blocks.
        ///   </para>
        /// </remarks>
        public void Reset()
        {
            this.accumulator = 0;
            this.nAccumulatedBits = 0;
            this.totalBytesWrittenOut = 0;
            this.output.Seek(0, SeekOrigin.Begin);
            this.output.SetLength(0);
        }
 
        /// <summary>
        ///   Write some number of bits from the given value, into the output.
        /// </summary>
        /// <remarks>
        ///   <para>
        ///     The nbits value should be a max of 25, for safety. For performance
        ///     reasons, this method does not check!
        ///   </para>
        /// </remarks>
        public void WriteBits(int nbits, uint value)
        {
            int nAccumulated = this.nAccumulatedBits;
            uint u = this.accumulator;
 
            while (nAccumulated >= 8)
            {
                this.output.WriteByte ((byte)(u >> 24 & 0xff));
                this.totalBytesWrittenOut++;
                u <<= 8;
                nAccumulated -= 8;
            }
 
            this.accumulator = u | (value << (32 - nAccumulated - nbits));
            this.nAccumulatedBits = nAccumulated + nbits;
 
            // Console.WriteLine("WriteBits({0}, 0x{1:X2}) => {2:X8} n({3})",
            //                   nbits, value, accumulator, nAccumulatedBits);
            // Console.ReadLine();
 
            // At this point the accumulator may contain up to 31 bits waiting for
            // output.
        }
 
 
        /// <summary>
        ///   Write a full 8-bit byte into the output.
        /// </summary>
        public void WriteByte(byte b)
        {
            WriteBits(8, b);
        }
 
        /// <summary>
        ///   Write four 8-bit bytes into the output.
        /// </summary>
        public void WriteInt(uint u)
        {
            WriteBits(8, (u >> 24) & 0xff);
            WriteBits(8, (u >> 16) & 0xff);
            WriteBits(8, (u >> 8) & 0xff);
            WriteBits(8, u & 0xff);
        }
 
        /// <summary>
        ///   Write all available byte-aligned bytes.
        /// </summary>
        /// <remarks>
        ///   <para>
        ///     This method writes no new output, but flushes any accumulated
        ///     bits. At completion, the accumulator may contain up to 7
        ///     bits.
        ///   </para>
        ///   <para>
        ///     This is necessary when re-assembling output from N independent
        ///     compressors, one for each of N blocks. The output of any
        ///     particular compressor will in general have some fragment of a byte
        ///     remaining. This fragment needs to be accumulated into the
        ///     parent BZip2OutputStream.
        ///   </para>
        /// </remarks>
        public void Flush()
        {
            WriteBits(0,0);
        }
 
 
        /// <summary>
        ///   Writes all available bytes, and emits padding for the final byte as
        ///   necessary. This must be the last method invoked on an instance of
        ///   BitWriter.
        /// </summary>
        public void FinishAndPad()
        {
            Flush();
 
            if (this.NumRemainingBits > 0)
            {
                byte b = (byte)((this.accumulator >> 24) & 0xff);
                this.output.WriteByte(b);
                this.totalBytesWrittenOut++;
            }
        }
 
    }
 
}