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  package org.waarp.compress.zlib;
22  
23  import io.netty.buffer.ByteBuf;
24  import org.waarp.common.file.FileUtils;
25  import org.waarp.compress.CompressorCodec;
26  import org.waarp.compress.MalformedInputException;
27  
28  import java.io.ByteArrayOutputStream;
29  import java.io.File;
30  import java.io.FileInputStream;
31  import java.io.FileOutputStream;
32  import java.io.IOException;
33  import java.io.InputStream;
34  import java.io.OutputStream;
35  import java.util.zip.DeflaterOutputStream;
36  import java.util.zip.InflaterOutputStream;
37  
38  /**
39   * ZSTD JNI Codec implementation
40   */
41  public class ZlibCodec implements CompressorCodec {
42  
43    @Override
44    public final int maxCompressedLength(final int uncompressedSize) {
45      return uncompressedSize;
46    }
47  
48    @Override
49    public final byte[] compress(final byte[] input, final int inputLength) {
50      ByteArrayOutputStream bos = null;
51      DeflaterOutputStream out = null;
52      try {
53        bos = new ByteArrayOutputStream(inputLength + 1024);
54        out = new DeflaterOutputStream(bos);
55        out.write(input, 0, inputLength);
56        out.close();
57        bos.close();
58        return bos.toByteArray();
59      } catch (final Exception e) {
60        throw new MalformedInputException(e);
61      } finally {
62        FileUtils.close(out);
63        FileUtils.close(bos);
64      }
65    }
66  
67    @Override
68    public final int compress(final byte[] input, final int inputLength,
69                              final byte[] output, final int maxOutputLength)
70        throws MalformedInputException {
71      try {
72        final byte[] bytes = compress(input, inputLength);
73        System.arraycopy(bytes, 0, output, 0, bytes.length);
74        return bytes.length;
75      } catch (final Exception e) {
76        throw new MalformedInputException(e);
77      }
78    }
79  
80    @Override
81    public final long compress(final File input, final File output)
82        throws MalformedInputException {
83      InputStream inputStream = null;
84      OutputStream outputStream = null;
85      DeflaterOutputStream out = null;
86      try {
87        inputStream = new FileInputStream(input);
88        outputStream = new FileOutputStream(output);
89        out = new DeflaterOutputStream(outputStream);
90        FileUtils.copy(64 * 1024, inputStream, out);
91        out = null;
92        return output.length();
93      } catch (final Exception e) {
94        throw new MalformedInputException(e);
95      } finally {
96        FileUtils.close(out);
97        FileUtils.close(inputStream);
98        FileUtils.close(outputStream);
99      }
100   }
101 
102   @Override
103   public final byte[] decompress(final byte[] compressed, final int length)
104       throws MalformedInputException {
105     ByteArrayOutputStream bos = null;
106     InflaterOutputStream out = null;
107     try {
108       bos = new ByteArrayOutputStream(length << 2);
109       out = new InflaterOutputStream(bos);
110       out.write(compressed, 0, length);
111       out.close();
112       bos.close();
113       return bos.toByteArray();
114     } catch (final Exception e) {
115       throw new MalformedInputException(e);
116     } finally {
117       FileUtils.close(out);
118       FileUtils.close(bos);
119     }
120   }
121 
122   @Override
123   public final int decompress(final byte[] input, final int inputLength,
124                               final byte[] output, final int maxOutputLength)
125       throws MalformedInputException {
126     try {
127       final byte[] bytes = decompress(input, inputLength);
128       System.arraycopy(bytes, 0, output, 0, bytes.length);
129       return bytes.length;
130     } catch (final Exception e) {
131       throw new MalformedInputException(e);
132     }
133   }
134 
135   @Override
136   public final long decompress(final File input, final File output)
137       throws MalformedInputException {
138     InputStream inputStream = null;
139     OutputStream outputStream = null;
140     InflaterOutputStream out = null;
141     try {
142       inputStream = new FileInputStream(input);
143       outputStream = new FileOutputStream(output);
144       out = new InflaterOutputStream(outputStream);
145       FileUtils.copy(64 * 1024, inputStream, out);
146       out = null;
147       return output.length();
148     } catch (final Exception e) {
149       throw new MalformedInputException(e);
150     } finally {
151       FileUtils.close(out);
152       FileUtils.close(inputStream);
153       FileUtils.close(outputStream);
154     }
155   }
156 
157   @Override
158   public final int getDecompressedSize(final byte[] compressed,
159                                        final int length) {
160     return 0;
161   }
162 
163   private ByteArrayOutputStream byteArrayOutputStream = null;
164   private DeflaterOutputStream deflaterOutputStream = null;
165   private InflaterOutputStream inflaterOutputStream = null;
166   private static final byte[] EMPTY_BYTES = {};
167 
168   /**
169    * Allow to specify a block size for CoDec
170    *
171    * @param defaultBlockSize
172    */
173   public void initializeCodecForStream(final int defaultBlockSize) {
174     byteArrayOutputStream = new ByteArrayOutputStream(defaultBlockSize);
175   }
176 
177   /**
178    * Write an uncompressed data for compression
179    *
180    * @param uncompressed
181    *
182    * @throws IOException
183    */
184   public synchronized void writeForCompression(final byte[] uncompressed)
185       throws IOException {
186     if (byteArrayOutputStream == null) {
187       byteArrayOutputStream = new ByteArrayOutputStream(1024 * 1024);
188     }
189     if (deflaterOutputStream == null) {
190       deflaterOutputStream = new DeflaterOutputStream(byteArrayOutputStream);
191     }
192     deflaterOutputStream.write(uncompressed);
193     deflaterOutputStream.flush();
194   }
195 
196   /**
197    * Write an uncompressed data for compression
198    *
199    * @param uncompressed
200    *
201    * @throws IOException
202    */
203   public void writeForCompression(final ByteBuf uncompressed)
204       throws IOException {
205     final byte[] buffer = new byte[uncompressed.readableBytes()];
206     uncompressed.readBytes(buffer);
207     writeForCompression(buffer);
208   }
209 
210   /**
211    * Write a compressed data for decompression
212    *
213    * @param compressed
214    *
215    * @throws IOException
216    */
217   public synchronized void writeForDecompression(final byte[] compressed)
218       throws IOException {
219     if (byteArrayOutputStream == null) {
220       byteArrayOutputStream = new ByteArrayOutputStream(1024 * 1024);
221     }
222     if (inflaterOutputStream == null) {
223       inflaterOutputStream = new InflaterOutputStream(byteArrayOutputStream);
224     }
225     inflaterOutputStream.write(compressed);
226     inflaterOutputStream.flush();
227   }
228 
229   /**
230    * Write a compressed data for decompression
231    *
232    * @param compressed
233    *
234    * @throws IOException
235    */
236   public synchronized void writeForDecompression(final ByteBuf compressed)
237       throws IOException {
238     final byte[] buffer = new byte[compressed.readableBytes()];
239     compressed.readBytes(buffer);
240     writeForDecompression(buffer);
241   }
242 
243   /**
244    * @return the current available block byte array (releasing it immediately)
245    */
246   public synchronized byte[] readCodec() {
247     if (deflaterOutputStream == null && inflaterOutputStream == null) {
248       return EMPTY_BYTES;
249     }
250     if (byteArrayOutputStream == null) {
251       return EMPTY_BYTES;
252     }
253     final byte[] bytes = byteArrayOutputStream.toByteArray();
254     byteArrayOutputStream.reset();
255     return bytes;
256   }
257 
258   /**
259    * Finish the CoDec operation. The Codec is ready to be reused.<br>
260    * <br>
261    * <b>Important: calling this method is mandatory to prevent OOME</b>
262    *
263    * @return the final block byte array
264    *
265    * @throws IOException
266    */
267   public synchronized byte[] finishCodec() throws IOException {
268     if (deflaterOutputStream != null) {
269       deflaterOutputStream.flush();
270       deflaterOutputStream.finish();
271       deflaterOutputStream.close();
272       deflaterOutputStream = null;
273     } else if (inflaterOutputStream != null) {
274       inflaterOutputStream.flush();
275       inflaterOutputStream.finish();
276       inflaterOutputStream.close();
277       inflaterOutputStream = null;
278     }
279     if (byteArrayOutputStream != null) {
280       final byte[] bytes = byteArrayOutputStream.toByteArray();
281       byteArrayOutputStream.close();
282       byteArrayOutputStream = null;
283       return bytes;
284     }
285     return EMPTY_BYTES;
286   }
287 }