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.zstdunsafe;
35  
36  import static org.waarp.compress.zstdunsafe.Constants.*;
37  
38  class HuffmanCompressor {
39    private HuffmanCompressor() {
40    }
41  
42    public static int compress4streams(final Object outputBase,
43                                       final long outputAddress,
44                                       final int outputSize,
45                                       final Object inputBase,
46                                       final long inputAddress,
47                                       final int inputSize,
48                                       final HuffmanCompressionTable table) {
49      long input = inputAddress;
50      final long inputLimit = inputAddress + inputSize;
51      long output = outputAddress;
52      final long outputLimit = outputAddress + outputSize;
53  
54      final int segmentSize = (inputSize + 3) / 4;
55  
56      if (outputSize <
57          6 /* jump table */ + 1 /* first stream */ + 1 /* second stream */ +
58          1 /* third stream */ +
59          8 /* 8 bytes minimum needed by the bitstream encoder */) {
60        return 0; // minimum space to compress successfully
61      }
62  
63      if (inputSize <= 6 + 1 + 1 + 1) { // jump table + one byte per stream
64        return 0;  // no saving possible: input too small
65      }
66  
67      output += SIZE_OF_SHORT + SIZE_OF_SHORT + SIZE_OF_SHORT; // jump table
68  
69      int compressedSize;
70  
71      // first segment
72      compressedSize =
73          compressSingleStream(outputBase, output, (int) (outputLimit - output),
74                               inputBase, input, segmentSize, table);
75      if (compressedSize == 0) {
76        return 0;
77      }
78      UnsafeUtil.UNSAFE.putShort(outputBase, outputAddress,
79                                 (short) compressedSize);
80      output += compressedSize;
81      input += segmentSize;
82  
83      // second segment
84      compressedSize =
85          compressSingleStream(outputBase, output, (int) (outputLimit - output),
86                               inputBase, input, segmentSize, table);
87      if (compressedSize == 0) {
88        return 0;
89      }
90      UnsafeUtil.UNSAFE.putShort(outputBase, outputAddress + SIZE_OF_SHORT,
91                                 (short) compressedSize);
92      output += compressedSize;
93      input += segmentSize;
94  
95      // third segment
96      compressedSize =
97          compressSingleStream(outputBase, output, (int) (outputLimit - output),
98                               inputBase, input, segmentSize, table);
99      if (compressedSize == 0) {
100       return 0;
101     }
102     UnsafeUtil.UNSAFE.putShort(outputBase,
103                                outputAddress + SIZE_OF_SHORT + SIZE_OF_SHORT,
104                                (short) compressedSize);
105     output += compressedSize;
106     input += segmentSize;
107 
108     // fourth segment
109     compressedSize =
110         compressSingleStream(outputBase, output, (int) (outputLimit - output),
111                              inputBase, input, (int) (inputLimit - input),
112                              table);
113     if (compressedSize == 0) {
114       return 0;
115     }
116     output += compressedSize;
117 
118     return (int) (output - outputAddress);
119   }
120 
121   public static int compressSingleStream(final Object outputBase,
122                                          final long outputAddress,
123                                          final int outputSize,
124                                          final Object inputBase,
125                                          final long inputAddress,
126                                          final int inputSize,
127                                          final HuffmanCompressionTable table) {
128     if (outputSize < SIZE_OF_LONG) {
129       return 0;
130     }
131     final BitOutputStream bitstream =
132         new BitOutputStream(outputBase, outputAddress, outputSize);
133 
134     int n = inputSize & ~3; // join to mod 4
135 
136     switch (inputSize & 3) {
137       case 3:
138         table.encodeSymbol(bitstream, UnsafeUtil.UNSAFE.getByte(inputBase,
139                                                                 inputAddress +
140                                                                 n + 2) & 0xFF);
141         // fall-through
142       case 2:
143         table.encodeSymbol(bitstream, UnsafeUtil.UNSAFE.getByte(inputBase,
144                                                                 inputAddress +
145                                                                 n + 1) & 0xFF);
146         // fall-through
147       case 1:
148         table.encodeSymbol(bitstream, UnsafeUtil.UNSAFE.getByte(inputBase,
149                                                                 inputAddress +
150                                                                 n) & 0xFF);
151         bitstream.flush();
152         // fall-through
153       case 0: /* fall-through */
154       default:
155         break;
156     }
157 
158     for (; n > 0; n -= 4) {  // note: n & 3 == 0 at this stage
159       table.encodeSymbol(bitstream, UnsafeUtil.UNSAFE.getByte(inputBase,
160                                                               inputAddress + n -
161                                                               1) & 0xFF);
162       table.encodeSymbol(bitstream, UnsafeUtil.UNSAFE.getByte(inputBase,
163                                                               inputAddress + n -
164                                                               2) & 0xFF);
165       table.encodeSymbol(bitstream, UnsafeUtil.UNSAFE.getByte(inputBase,
166                                                               inputAddress + n -
167                                                               3) & 0xFF);
168       table.encodeSymbol(bitstream, UnsafeUtil.UNSAFE.getByte(inputBase,
169                                                               inputAddress + n -
170                                                               4) & 0xFF);
171       bitstream.flush();
172     }
173 
174     return bitstream.close();
175   }
176 }