View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.compress.compressors.deflate64;
20  
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.junit.jupiter.api.Assertions.fail;
24  
25  import java.io.ByteArrayInputStream;
26  import java.util.Arrays;
27  
28  import org.junit.jupiter.api.Test;
29  
30  class HuffmanDecoderTest {
31  
32      @Test
33      void testDecodeFixedHuffmanBlockWithMemoryLookup() throws Exception {
34          final byte[] data = {
35                  // |--- binary filling ---|76543210
36                  0b11111111111111111111111111110011, // final block + fixed huffman + H
37                  0b00000000000000000000000001001000, // H + e
38                  0b11111111111111111111111111001101, // e + l
39                  0b11111111111111111111111111001001, // l + l
40                  0b11111111111111111111111111001001, // l + o
41                  0b00000000000000000000000001010111, // o + ' '
42                  0b00000000000000000000000000001000, // ' ' + W
43                  0b11111111111111111111111111001111, // W + o
44                  0b00000000000000000000000000101111, // o + r
45                  0b11111111111111111111111111001010, // r + l
46                  0b00000000000000000000000001001001, // l + d
47                  0b11111111111111111111111111100001, // d + '\n'
48                  0b00000000000000000000000000100010, // '\n' + <len>
49                  0b11111111111111111111111110000110, // <len> + offset <001> + dist6
50                  0b00000000000000000000000000001101, // dist6 + offset <11> + end of block (000000)
51                  0b11111111111111111111111111111000 // end of block (0000) + garbage
52          };
53          try (HuffmanDecoder decoder = new HuffmanDecoder(new ByteArrayInputStream(data))) {
54              final byte[] result = new byte[100];
55              final int len = decoder.decode(result);
56              assertEquals(48, len);
57              assertEquals("Hello World\nHello World\nHello World\nHello World\n", new String(result, 0, len));
58          }
59      }
60  
61      @Test
62      void testDecodeFixedHuffmanBlockWithMemoryLookupInExactBuffer() throws Exception {
63          final byte[] data = {
64                  // |--- binary filling ---|76543210
65                  0b11111111111111111111111111110011, // final block + fixed huffman + H
66                  0b00000000000000000000000001001000, // H + e
67                  0b11111111111111111111111111001101, // e + l
68                  0b11111111111111111111111111001001, // l + l
69                  0b11111111111111111111111111001001, // l + o
70                  0b00000000000000000000000001010111, // o + ' '
71                  0b00000000000000000000000000001000, // ' ' + W
72                  0b11111111111111111111111111001111, // W + o
73                  0b00000000000000000000000000101111, // o + r
74                  0b11111111111111111111111111001010, // r + l
75                  0b00000000000000000000000001001001, // l + d
76                  0b11111111111111111111111111100001, // d + '\n'
77                  0b00000000000000000000000000100010, // '\n' + <len>
78                  0b11111111111111111111111110000110, // <len> + offset <001> + dist6
79                  0b00000000000000000000000000001101, // dist6 + offset <11> + end of block (000000)
80                  0b11111111111111111111111111111000 // end of block (0000) + garbage
81          };
82          try (HuffmanDecoder decoder = new HuffmanDecoder(new ByteArrayInputStream(data))) {
83              final byte[] result = new byte[48];
84              int len;
85              len = decoder.decode(result);
86              assertEquals(48, len);
87              assertEquals("Hello World\nHello World\nHello World\nHello World\n", new String(result, 0, len));
88              len = decoder.decode(result);
89              assertEquals(-1, len);
90          }
91      }
92  
93      @Test
94      void testDecodeFixedHuffmanBlockWithMemoryLookupInSmallBuffer() throws Exception {
95          final byte[] data = {
96                  // |--- binary filling ---|76543210
97                  0b11111111111111111111111111110011, // final block + fixed huffman + H
98                  0b00000000000000000000000001001000, // H + e
99                  0b11111111111111111111111111001101, // e + l
100                 0b11111111111111111111111111001001, // l + l
101                 0b11111111111111111111111111001001, // l + o
102                 0b00000000000000000000000001010111, // o + ' '
103                 0b00000000000000000000000000001000, // ' ' + W
104                 0b11111111111111111111111111001111, // W + o
105                 0b00000000000000000000000000101111, // o + r
106                 0b11111111111111111111111111001010, // r + l
107                 0b00000000000000000000000001001001, // l + d
108                 0b11111111111111111111111111100001, // d + '\n'
109                 0b00000000000000000000000000100010, // '\n' + <len>
110                 0b11111111111111111111111110000110, // <len> + offset <001> + dist6
111                 0b00000000000000000000000000001101, // dist6 + offset <11> + end of block (000000)
112                 0b11111111111111111111111111111000 // end of block (0000) + garbage
113         };
114 
115         try (HuffmanDecoder decoder = new HuffmanDecoder(new ByteArrayInputStream(data))) {
116             final byte[] result = new byte[30];
117             int len;
118             len = decoder.decode(result);
119             assertEquals(30, len);
120             assertEquals("Hello World\nHello World\nHello ", new String(result, 0, len));
121             len = decoder.decode(result);
122             assertEquals(18, len);
123             assertEquals("World\nHello World\n", new String(result, 0, len));
124         }
125     }
126 
127     @Test
128     void testDecodeSimpleFixedHuffmanBlock() throws Exception {
129         final byte[] data = {
130                 // |--- binary filling ---|76543210
131                 0b11111111111111111111111111110011, // final block + fixed huffman + H
132                 0b00000000000000000000000001001000, // H + e
133                 0b11111111111111111111111111001101, // e + l
134                 0b11111111111111111111111111001001, // l + l
135                 0b11111111111111111111111111001001, // l + o
136                 0b00000000000000000000000001010111, // o + ' '
137                 0b00000000000000000000000000001000, // ' ' + W
138                 0b11111111111111111111111111001111, // W + o
139                 0b00000000000000000000000000101111, // o + r
140                 0b11111111111111111111111111001010, // r + l
141                 0b00000000000000000000000001001001, // l + d
142                 0b00000000000000000000000000000001, // d + end of block
143                 0b11111111111111111111111111111100 // end of block (00) + garbage
144         };
145         try (HuffmanDecoder decoder = new HuffmanDecoder(new ByteArrayInputStream(data))) {
146             final byte[] result = new byte[100];
147             final int len = decoder.decode(result);
148             assertEquals(11, len);
149             assertEquals("Hello World", new String(result, 0, len));
150         }
151     }
152 
153     @Test
154     void testDecodeSimpleFixedHuffmanBlockToSmallBuffer() throws Exception {
155         final byte[] data = {
156                 // |--- binary filling ---|76543210
157                 0b11111111111111111111111111110011, // final block + fixed huffman + H
158                 0b00000000000000000000000001001000, // H + e
159                 0b11111111111111111111111111001101, // e + l
160                 0b11111111111111111111111111001001, // l + l
161                 0b11111111111111111111111111001001, // l + o
162                 0b00000000000000000000000001010111, // o + ' '
163                 0b00000000000000000000000000001000, // ' ' + W
164                 0b11111111111111111111111111001111, // W + o
165                 0b00000000000000000000000000101111, // o + r
166                 0b11111111111111111111111111001010, // r + l
167                 0b00000000000000000000000001001001, // l + d
168                 0b00000000000000000000000000000001, // d + end of block
169                 0b11111111111111111111111111111100 // end of block (00) + garbage
170         };
171         try (HuffmanDecoder decoder = new HuffmanDecoder(new ByteArrayInputStream(data))) {
172             final byte[] result = new byte[10];
173             int len;
174             len = decoder.decode(result);
175             assertEquals(10, len);
176             assertEquals("Hello Worl", new String(result, 0, len));
177             len = decoder.decode(result);
178             assertEquals(1, len);
179             assertEquals("d", new String(result, 0, len));
180         }
181     }
182 
183     @Test
184     void testDecodeUncompressedBlock() throws Exception {
185         final byte[] data = { 0b1, // end of block + no compression mode
186                 11, 0, -12, -1, // len & ~len
187                 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' };
188         try (HuffmanDecoder decoder = new HuffmanDecoder(new ByteArrayInputStream(data))) {
189             final byte[] result = new byte[100];
190             final int len = decoder.decode(result);
191             assertEquals(11, len);
192             assertEquals("Hello World", new String(result, 0, len));
193         }
194     }
195 
196     @Test
197     void testDecodeUncompressedBlockWithInvalidLenNLenValue() throws Exception {
198         final byte[] data = { 0b1, // end of block + no compression mode
199                 11, 0, -12, -2, // len & ~len
200                 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' };
201         try (HuffmanDecoder decoder = new HuffmanDecoder(new ByteArrayInputStream(data))) {
202             final byte[] result = new byte[100];
203             final IllegalStateException e = assertThrows(IllegalStateException.class, () -> {
204                 final int len = decoder.decode(result);
205                 fail("Should have failed but returned " + len + " entries: " + Arrays.toString(Arrays.copyOf(result, len)));
206             });
207             assertEquals("Illegal LEN / NLEN values", e.getMessage());
208         }
209     }
210 }