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.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       * 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(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; // this should be EOS symbol
107             out.writeByte((int) current);
108         }
109     }
110 
111     /**
112      * Returns the number of bytes required to Huffman encode the input string literal.
113      *
114      * @param data the string literal to be Huffman encoded
115      * @return the number of bytes required to Huffman encode {@code data}
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; // this should be EOS symbol
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 }