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 package io.netty.handler.codec.http2;
33
34 import io.netty.buffer.ByteBuf;
35 import io.netty.util.AsciiString;
36 import io.netty.util.ByteProcessor;
37 import io.netty.util.internal.ObjectUtil;
38 import io.netty.util.internal.PlatformDependent;
39
40 final class HpackHuffmanEncoder {
41
42 private final int[] codes;
43 private final byte[] lengths;
44 private final EncodedLengthProcessor encodedLengthProcessor = new EncodedLengthProcessor();
45 private final EncodeProcessor encodeProcessor = new EncodeProcessor();
46
47 HpackHuffmanEncoder() {
48 this(HpackUtil.HUFFMAN_CODES, HpackUtil.HUFFMAN_CODE_LENGTHS);
49 }
50
51
52
53
54
55
56
57 private HpackHuffmanEncoder(int[] codes, byte[] lengths) {
58 this.codes = codes;
59 this.lengths = lengths;
60 }
61
62
63
64
65
66
67
68 public void encode(ByteBuf out, CharSequence data) {
69 ObjectUtil.checkNotNull(out, "out");
70 if (data instanceof AsciiString) {
71 AsciiString string = (AsciiString) data;
72 try {
73 encodeProcessor.out = out;
74 string.forEachByte(encodeProcessor);
75 } catch (Exception e) {
76 PlatformDependent.throwException(e);
77 } finally {
78 encodeProcessor.end();
79 }
80 } else {
81 encodeSlowPath(out, data);
82 }
83 }
84
85 private void encodeSlowPath(ByteBuf out, CharSequence data) {
86 long current = 0;
87 int n = 0;
88
89 for (int i = 0; i < data.length(); i++) {
90 int b = AsciiString.c2b(data.charAt(i)) & 0xFF;
91 int code = codes[b];
92 int nbits = lengths[b];
93
94 current <<= nbits;
95 current |= code;
96 n += nbits;
97
98 while (n >= 8) {
99 n -= 8;
100 out.writeByte((int) (current >> n));
101 }
102 }
103
104 if (n > 0) {
105 current <<= 8 - n;
106 current |= 0xFF >>> n;
107 out.writeByte((int) current);
108 }
109 }
110
111
112
113
114
115
116
117 int getEncodedLength(CharSequence data) {
118 if (data instanceof AsciiString) {
119 AsciiString string = (AsciiString) data;
120 try {
121 encodedLengthProcessor.reset();
122 string.forEachByte(encodedLengthProcessor);
123 return encodedLengthProcessor.length();
124 } catch (Exception e) {
125 PlatformDependent.throwException(e);
126 return -1;
127 }
128 } else {
129 return getEncodedLengthSlowPath(data);
130 }
131 }
132
133 private int getEncodedLengthSlowPath(CharSequence data) {
134 long len = 0;
135 for (int i = 0; i < data.length(); i++) {
136 len += lengths[AsciiString.c2b(data.charAt(i)) & 0xFF];
137 }
138 return (int) (len + 7 >> 3);
139 }
140
141 private final class EncodeProcessor implements ByteProcessor {
142 ByteBuf out;
143 private long current;
144 private int n;
145
146 @Override
147 public boolean process(byte value) {
148 int b = value & 0xFF;
149 int nbits = lengths[b];
150
151 current <<= nbits;
152 current |= codes[b];
153 n += nbits;
154
155 while (n >= 8) {
156 n -= 8;
157 out.writeByte((int) (current >> n));
158 }
159 return true;
160 }
161
162 void end() {
163 try {
164 if (n > 0) {
165 current <<= 8 - n;
166 current |= 0xFF >>> n;
167 out.writeByte((int) current);
168 }
169 } finally {
170 out = null;
171 current = 0;
172 n = 0;
173 }
174 }
175 }
176
177 private final class EncodedLengthProcessor implements ByteProcessor {
178 private long len;
179
180 @Override
181 public boolean process(byte value) {
182 len += lengths[value & 0xFF];
183 return true;
184 }
185
186 void reset() {
187 len = 0;
188 }
189
190 int length() {
191 return (int) ((len + 7) >> 3);
192 }
193 }
194 }