View Javadoc
1   /*
2    * Copyright 2015 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  
17  /*
18   * Copyright 2014 Twitter, Inc.
19   *
20   * Licensed under the Apache License, Version 2.0 (the "License");
21   * you may not use this file except in compliance with the License.
22   * You may obtain a copy of the License at
23   *
24   *     https://www.apache.org/licenses/LICENSE-2.0
25   *
26   * Unless required by applicable law or agreed to in writing, software
27   * distributed under the License is distributed on an "AS IS" BASIS,
28   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29   * See the License for the specific language governing permissions and
30   * limitations under the License.
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       * Creates a new Huffman encoder with the specified Huffman coding.
53       *
54       * @param codes the Huffman codes indexed by symbol
55       * @param lengths the length of each Huffman code
56       */
57      private HpackHuffmanEncoder(int[] codes, byte[] lengths) {
58          this.codes = codes;
59          this.lengths = lengths;
60      }
61  
62      /**
63       * Compresses the input string literal using the Huffman coding.
64       *
65       * @param out the output stream for the compressed data
66       * @param data the string literal to be Huffman encoded
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; // this should be EOS symbol
105             out.writeByte((byte) current);
106         }
107     }
108 
109     /**
110      * Returns the number of bytes required to Huffman encode the input string literal.
111      *
112      * @param data the string literal to be Huffman encoded
113      * @return the number of bytes required to Huffman encode {@code data}
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; // this should be EOS symbol
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 }