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.netty5.handler.codec.http2;
33
34 import io.netty5.buffer.api.Buffer;
35 import io.netty5.util.AsciiString;
36 import io.netty5.util.ByteProcessor;
37
38 import static java.util.Objects.requireNonNull;
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(Buffer out, CharSequence data) {
69 requireNonNull(out, "out");
70 if (data instanceof AsciiString) {
71 AsciiString string = (AsciiString) data;
72 encodeProcessor.out = out;
73 try {
74 string.forEachByte(encodeProcessor);
75 } finally {
76 encodeProcessor.end();
77 }
78 } else {
79 encodeSlowPath(out, data);
80 }
81 }
82
83 private void encodeSlowPath(Buffer out, CharSequence data) {
84 long current = 0;
85 int n = 0;
86
87 for (int i = 0; i < data.length(); i++) {
88 int b = data.charAt(i) & 0xFF;
89 int code = codes[b];
90 int nbits = lengths[b];
91
92 current <<= nbits;
93 current |= code;
94 n += nbits;
95
96 while (n >= 8) {
97 n -= 8;
98 out.writeByte((byte) (current >> n));
99 }
100 }
101
102 if (n > 0) {
103 current <<= 8 - n;
104 current |= 0xFF >>> n;
105 out.writeByte((byte) current);
106 }
107 }
108
109
110
111
112
113
114
115 int getEncodedLength(CharSequence data) {
116 if (data instanceof AsciiString) {
117 AsciiString string = (AsciiString) data;
118 encodedLengthProcessor.reset();
119 string.forEachByte(encodedLengthProcessor);
120 return encodedLengthProcessor.length();
121 } else {
122 return getEncodedLengthSlowPath(data);
123 }
124 }
125
126 private int getEncodedLengthSlowPath(CharSequence data) {
127 long len = 0;
128 for (int i = 0; i < data.length(); i++) {
129 len += lengths[data.charAt(i) & 0xFF];
130 }
131 return (int) (len + 7 >> 3);
132 }
133
134 private final class EncodeProcessor implements ByteProcessor {
135 Buffer out;
136 private long current;
137 private int n;
138
139 @Override
140 public boolean process(byte value) {
141 int b = value & 0xFF;
142 int nbits = lengths[b];
143
144 current <<= nbits;
145 current |= codes[b];
146 n += nbits;
147
148 while (n >= 8) {
149 n -= 8;
150 out.writeByte((byte) (current >> n));
151 }
152 return true;
153 }
154
155 void end() {
156 try {
157 if (n > 0) {
158 current <<= 8 - n;
159 current |= 0xFF >>> n;
160 out.writeByte((byte) current);
161 }
162 } finally {
163 out = null;
164 current = 0;
165 n = 0;
166 }
167 }
168 }
169
170 private final class EncodedLengthProcessor implements ByteProcessor {
171 private long len;
172
173 @Override
174 public boolean process(byte value) {
175 len += lengths[value & 0xFF];
176 return true;
177 }
178
179 void reset() {
180 len = 0;
181 }
182
183 int length() {
184 return (int) (len + 7 >> 3);
185 }
186 }
187 }