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.netty5.handler.codec.compression;
17
18 import io.netty5.buffer.api.Buffer;
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 Buffer} 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 Buffer}.
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(Buffer 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 Buffer}.
60 * @param value The bit to write
61 */
62 void writeBoolean(Buffer 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 Buffer}.
77 * Example of the output for value = 6: {@code 1111110}
78 * @param value The number of {@code 1} to write
79 */
80 void writeUnary(Buffer 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 Buffer}.
92 * @param value The integer to write
93 */
94 void writeInt(Buffer out, final int value) {
95 writeBits(out, 32, value);
96 }
97
98 /**
99 * Writes any remaining bits to the output {@link Buffer},
100 * zero padding to a whole byte as required.
101 */
102 void flush(Buffer 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((byte) (bitBuffer >>> shiftToRight << 8 - bitCount));
111 } else if (bitCount <= 16) {
112 out.writeShort((short) (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 }