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