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 package org.apache.hc.core5.http2.hpack;
29
30 import java.nio.ByteBuffer;
31
32 import org.apache.hc.core5.util.ByteArrayBuffer;
33
34
35
36
37
38 final class HuffmanDecoder {
39
40 private final HuffmanNode root;
41
42 HuffmanDecoder(final int[] codes, final byte[] lengths) {
43 root = buildTree(codes, lengths);
44 }
45
46 void decode(final ByteArrayBuffer out, final ByteBuffer src) throws HPackException {
47 HuffmanNode node = this.root;
48 int current = 0;
49 int bits = 0;
50 while (src.hasRemaining()) {
51 final int b = src.get() & 0xFF;
52 current = (current << 8) | b;
53 bits += 8;
54 while (bits >= 8) {
55 final int c = (current >>> (bits - 8)) & 0xFF;
56 node = node.getChild(c);
57 bits -= node.getBits();
58 if (node.isTerminal()) {
59 if (node.getSymbol() == Huffman.EOS) {
60 throw new HPackException("EOS decoded");
61 }
62 out.append(node.getSymbol());
63 node = root;
64 }
65 }
66 }
67
68 while (bits > 0) {
69 final int c = (current << (8 - bits)) & 0xFF;
70 node = node.getChild(c);
71 if (node.isTerminal() && node.getBits() <= bits) {
72 bits -= node.getBits();
73 out.append(node.getSymbol());
74 node = this.root;
75 } else {
76 break;
77 }
78 }
79
80
81
82
83 final int mask = (1 << bits) - 1;
84 if ((current & mask) != mask) {
85 throw new HPackException("Invalid padding");
86 }
87 }
88
89 private static HuffmanNode buildTree(final int[] codes, final byte[] lengths) {
90 final HuffmanNode root = new HuffmanNode();
91 for (int symbol = 0; symbol < codes.length; symbol++) {
92
93 final int code = codes[symbol];
94 int length = lengths[symbol];
95
96 HuffmanNode current = root;
97 while (length > 8) {
98 if (current.isTerminal()) {
99 throw new IllegalStateException("Invalid Huffman code: prefix not unique");
100 }
101 length -= 8;
102 final int i = (code >>> length) & 0xFF;
103 if (!current.hasChild(i)) {
104 current.setChild(i, new HuffmanNode());
105 }
106 current = current.getChild(i);
107 }
108
109 final HuffmanNode terminal = new HuffmanNode(symbol, length);
110 final int shift = 8 - length;
111 final int start = (code << shift) & 0xFF;
112 final int end = 1 << shift;
113 for (int i = start; i < start + end; i++) {
114 current.setChild(i, terminal);
115 }
116 }
117 return root;
118 }
119
120 }