1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http3;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.util.AsciiString;
20 import io.netty.util.ByteProcessor;
21 import io.netty.util.internal.ObjectUtil;
22
23 final class QpackHuffmanEncoder {
24
25 private final int[] codes;
26 private final byte[] lengths;
27 private final EncodedLengthProcessor encodedLengthProcessor = new EncodedLengthProcessor();
28 private final EncodeProcessor encodeProcessor = new EncodeProcessor();
29
30 QpackHuffmanEncoder() {
31 this(QpackUtil.HUFFMAN_CODES, QpackUtil.HUFFMAN_CODE_LENGTHS);
32 }
33
34
35
36
37
38
39
40 private QpackHuffmanEncoder(int[] codes, byte[] lengths) {
41 this.codes = codes;
42 this.lengths = lengths;
43 }
44
45
46
47
48
49
50
51 public void encode(ByteBuf out, CharSequence data) {
52 ObjectUtil.checkNotNull(out, "out");
53 if (data instanceof AsciiString) {
54 AsciiString string = (AsciiString) data;
55 try {
56 encodeProcessor.out = out;
57 string.forEachByte(encodeProcessor);
58 } catch (Exception e) {
59 throw new IllegalStateException(e);
60 } finally {
61 encodeProcessor.end();
62 }
63 } else {
64 encodeSlowPath(out, data);
65 }
66 }
67
68 private void encodeSlowPath(ByteBuf out, CharSequence data) {
69 long current = 0;
70 int n = 0;
71
72 for (int i = 0; i < data.length(); i++) {
73 int b = data.charAt(i) & 0xFF;
74 int code = codes[b];
75 int nbits = lengths[b];
76
77 current <<= nbits;
78 current |= code;
79 n += nbits;
80
81 while (n >= 8) {
82 n -= 8;
83 out.writeByte((int) (current >> n));
84 }
85 }
86
87 if (n > 0) {
88 current <<= 8 - n;
89 current |= 0xFF >>> n;
90 out.writeByte((int) current);
91 }
92 }
93
94
95
96
97
98
99
100 int getEncodedLength(CharSequence data) {
101 if (data instanceof AsciiString) {
102 AsciiString string = (AsciiString) data;
103 try {
104 encodedLengthProcessor.reset();
105 string.forEachByte(encodedLengthProcessor);
106 return encodedLengthProcessor.length();
107 } catch (Exception e) {
108 throw new IllegalStateException(e);
109 }
110 } else {
111 return getEncodedLengthSlowPath(data);
112 }
113 }
114
115 private int getEncodedLengthSlowPath(CharSequence data) {
116 long len = 0;
117 for (int i = 0; i < data.length(); i++) {
118 len += lengths[data.charAt(i) & 0xFF];
119 }
120 return (int) ((len + 7) >> 3);
121 }
122
123 private final class EncodeProcessor implements ByteProcessor {
124 ByteBuf out;
125 private long current;
126 private int n;
127
128 @Override
129 public boolean process(byte value) {
130 int b = value & 0xFF;
131 int nbits = lengths[b];
132
133 current <<= nbits;
134 current |= codes[b];
135 n += nbits;
136
137 while (n >= 8) {
138 n -= 8;
139 out.writeByte((int) (current >> n));
140 }
141 return true;
142 }
143
144 void end() {
145 try {
146 if (n > 0) {
147 current <<= 8 - n;
148 current |= 0xFF >>> n;
149 out.writeByte((int) current);
150 }
151 } finally {
152 out = null;
153 current = 0;
154 n = 0;
155 }
156 }
157 }
158
159 private final class EncodedLengthProcessor implements ByteProcessor {
160 private long len;
161
162 @Override
163 public boolean process(byte value) {
164 len += lengths[value & 0xFF];
165 return true;
166 }
167
168 void reset() {
169 len = 0;
170 }
171
172 int length() {
173 return (int) ((len + 7) >> 3);
174 }
175 }
176 }