View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  
21  /*
22   * Licensed under the Apache License, Version 2.0 (the "License");
23   * you may not use this file except in compliance with the License.
24   * You may obtain a copy of the License at
25   *
26   *     http://www.apache.org/licenses/LICENSE-2.0
27   *
28   * Unless required by applicable law or agreed to in writing, software
29   * distributed under the License is distributed on an "AS IS" BASIS,
30   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31   * See the License for the specific language governing permissions and
32   * limitations under the License.
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 /* jump table */ + 1 /* first stream */ + 1 /* second stream */ +
59          1 /* third stream */ +
60          8 /* 8 bytes minimum needed by the bitstream encoder */) {
61        return 0; // minimum space to compress successfully
62      }
63  
64      if (inputSize <= 6 + 1 + 1 + 1) { // jump table + one byte per stream
65        return 0;  // no saving possible: input too small
66      }
67  
68      output += SIZE_OF_SHORT + SIZE_OF_SHORT + SIZE_OF_SHORT; // jump table
69  
70      int compressedSize;
71  
72      // first segment
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      // second segment
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      // third segment
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     // fourth segment
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; // join to mod 4
132 
133     switch (inputSize & 3) {
134       case 3:
135         table.encodeSymbol(bitstream, inputBase[inputAddress + n + 2] & 0xFF);
136         // fall-through
137       case 2:
138         table.encodeSymbol(bitstream, inputBase[inputAddress + n + 1] & 0xFF);
139         // fall-through
140       case 1:
141         table.encodeSymbol(bitstream, inputBase[inputAddress + n] & 0xFF);
142         bitstream.flush();
143         // fall-through
144       case 0: /* fall-through */
145       default:
146         break;
147     }
148 
149     for (; n > 0; n -= 4) {  // note: n & 3 == 0 at this stage
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 }