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  
29  
30  
31  
32  
33  
34  package org.waarp.compress.zstdsafe;
35  
36  import static org.waarp.compress.zstdsafe.Constants.*;
37  import static org.waarp.compress.zstdsafe.UnsafeUtil.*;
38  
39  class HuffmanCompressor {
40    private HuffmanCompressor() {
41    }
42  
43    public static int compress4streams(final byte[] outputBase,
44                                       final int outputAddress,
45                                       final int outputSize,
46                                       final byte[] inputBase,
47                                       final int inputAddress,
48                                       final int inputSize,
49                                       final HuffmanCompressionTable table) {
50      int input = inputAddress;
51      final int inputLimit = inputAddress + inputSize;
52      int output = outputAddress;
53      final int outputLimit = outputAddress + outputSize;
54  
55      final int segmentSize = (inputSize + 3) / 4;
56  
57      if (outputSize <
58          6  + 1  + 1  +
59          1  +
60          8 ) {
61        return 0; 
62      }
63  
64      if (inputSize <= 6 + 1 + 1 + 1) { 
65        return 0;  
66      }
67  
68      output += SIZE_OF_SHORT + SIZE_OF_SHORT + SIZE_OF_SHORT; 
69  
70      int compressedSize;
71  
72      
73      compressedSize =
74          compressSingleStream(outputBase, output, outputLimit - output,
75                               inputBase, input, segmentSize, table);
76      if (compressedSize == 0) {
77        return 0;
78      }
79      putShort(outputBase, outputAddress, (short) compressedSize);
80      output += compressedSize;
81      input += segmentSize;
82  
83      
84      compressedSize =
85          compressSingleStream(outputBase, output, outputLimit - output,
86                               inputBase, input, segmentSize, table);
87      if (compressedSize == 0) {
88        return 0;
89      }
90      putShort(outputBase, outputAddress + SIZE_OF_SHORT, (short) compressedSize);
91      output += compressedSize;
92      input += segmentSize;
93  
94      
95      compressedSize =
96          compressSingleStream(outputBase, output, outputLimit - output,
97                               inputBase, input, segmentSize, table);
98      if (compressedSize == 0) {
99        return 0;
100     }
101     putShort(outputBase, outputAddress + SIZE_OF_SHORT + SIZE_OF_SHORT,
102              (short) compressedSize);
103     output += compressedSize;
104     input += segmentSize;
105 
106     
107     compressedSize =
108         compressSingleStream(outputBase, output, outputLimit - output,
109                              inputBase, input, inputLimit - input, table);
110     if (compressedSize == 0) {
111       return 0;
112     }
113     output += compressedSize;
114 
115     return output - outputAddress;
116   }
117 
118   public static int compressSingleStream(final byte[] outputBase,
119                                          final int outputAddress,
120                                          final int outputSize,
121                                          final byte[] inputBase,
122                                          final int inputAddress,
123                                          final int inputSize,
124                                          final HuffmanCompressionTable table) {
125     if (outputSize < SIZE_OF_LONG) {
126       return 0;
127     }
128     final BitOutputStream bitstream =
129         new BitOutputStream(outputBase, outputAddress, outputSize);
130 
131     int n = inputSize & ~3; 
132 
133     switch (inputSize & 3) {
134       case 3:
135         table.encodeSymbol(bitstream, inputBase[inputAddress + n + 2] & 0xFF);
136         
137       case 2:
138         table.encodeSymbol(bitstream, inputBase[inputAddress + n + 1] & 0xFF);
139         
140       case 1:
141         table.encodeSymbol(bitstream, inputBase[inputAddress + n] & 0xFF);
142         bitstream.flush();
143         
144       case 0: 
145       default:
146         break;
147     }
148 
149     for (; n > 0; n -= 4) {  
150       table.encodeSymbol(bitstream, inputBase[inputAddress + n - 1] & 0xFF);
151       table.encodeSymbol(bitstream, inputBase[inputAddress + n - 2] & 0xFF);
152       table.encodeSymbol(bitstream, inputBase[inputAddress + n - 3] & 0xFF);
153       table.encodeSymbol(bitstream, inputBase[inputAddress + n - 4] & 0xFF);
154       bitstream.flush();
155     }
156 
157     return bitstream.close();
158   }
159 }