View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.codec.compression;
17  
18  import io.netty.buffer.ByteBuf;
19  
20  /**
21   * A bit writer that allows the writing of single bit booleans, unary numbers, bit strings
22   * of arbitrary length (up to 32 bits), and bit aligned 32-bit integers. A single byte at a
23   * time is written to the {@link ByteBuf} when sufficient bits have been accumulated.
24   */
25  final class Bzip2BitWriter {
26      /**
27       * A buffer of bits waiting to be written to the output stream.
28       */
29      private long bitBuffer;
30  
31      /**
32       * The number of bits currently buffered in {@link #bitBuffer}.
33       */
34      private int bitCount;
35  
36      /**
37       * Writes up to 32 bits to the output {@link ByteBuf}.
38       * @param count The number of bits to write (maximum {@code 32} as a size of {@code int})
39       * @param value The bits to write
40       */
41      void writeBits(ByteBuf out, final int count, final long value) {
42          if (count < 0 || count > 32) {
43              throw new IllegalArgumentException("count: " + count + " (expected: 0-32)");
44          }
45          int bitCount = this.bitCount;
46          long bitBuffer = this.bitBuffer | value << 64 - count >>> bitCount;
47          bitCount += count;
48  
49          if (bitCount >= 32) {
50              out.writeInt((int) (bitBuffer >>> 32));
51              bitBuffer <<= 32;
52              bitCount -= 32;
53          }
54          this.bitBuffer = bitBuffer;
55          this.bitCount = bitCount;
56      }
57  
58      /**
59       * Writes a single bit to the output {@link ByteBuf}.
60       * @param value The bit to write
61       */
62      void writeBoolean(ByteBuf out, final boolean value) {
63          int bitCount = this.bitCount + 1;
64          long bitBuffer = this.bitBuffer | (value ? 1L << 64 - bitCount : 0L);
65  
66          if (bitCount == 32) {
67              out.writeInt((int) (bitBuffer >>> 32));
68              bitBuffer = 0;
69              bitCount = 0;
70          }
71          this.bitBuffer = bitBuffer;
72          this.bitCount = bitCount;
73      }
74  
75      /**
76       * Writes a zero-terminated unary number to the output {@link ByteBuf}.
77       * Example of the output for value = 6: {@code 1111110}
78       * @param value The number of {@code 1} to write
79       */
80      void writeUnary(ByteBuf out, int value) {
81          if (value < 0) {
82              throw new IllegalArgumentException("value: " + value + " (expected 0 or more)");
83          }
84          while (value-- > 0) {
85              writeBoolean(out, true);
86          }
87          writeBoolean(out, false);
88      }
89  
90      /**
91       * Writes an integer as 32 bits to the output {@link ByteBuf}.
92       * @param value The integer to write
93       */
94      void writeInt(ByteBuf out, final int value) {
95          writeBits(out, 32, value);
96      }
97  
98      /**
99       * Writes any remaining bits to the output {@link ByteBuf},
100      * zero padding to a whole byte as required.
101      */
102     void flush(ByteBuf out) {
103         final int bitCount = this.bitCount;
104 
105         if (bitCount > 0) {
106             final long bitBuffer = this.bitBuffer;
107             final int shiftToRight = 64 - bitCount;
108 
109             if (bitCount <= 8) {
110                 out.writeByte((int) (bitBuffer >>> shiftToRight << 8 - bitCount));
111             } else if (bitCount <= 16) {
112                 out.writeShort((int) (bitBuffer >>> shiftToRight << 16 - bitCount));
113             } else if (bitCount <= 24) {
114                 out.writeMedium((int) (bitBuffer >>> shiftToRight << 24 - bitCount));
115             } else {
116                 out.writeInt((int) (bitBuffer >>> shiftToRight << 32 - bitCount));
117             }
118         }
119     }
120 }