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 }