View Javadoc
1   package nom.tam.fits.compression.algorithm.gzip2;
2   
3   /*
4    * #%L
5    * nom.tam FITS library
6    * %%
7    * Copyright (C) 1996 - 2024 nom-tam-fits
8    * %%
9    * This is free and unencumbered software released into the public domain.
10   *
11   * Anyone is free to copy, modify, publish, use, compile, sell, or
12   * distribute this software, either in source code form or as a compiled
13   * binary, for any purpose, commercial or non-commercial, and by any
14   * means.
15   *
16   * In jurisdictions that recognize copyright laws, the author or authors
17   * of this software dedicate any and all copyright interest in the
18   * software to the public domain. We make this dedication for the benefit
19   * of the public at large and to the detriment of our heirs and
20   * successors. We intend this dedication to be an overt act of
21   * relinquishment in perpetuity of all present and future rights to this
22   * software under copyright law.
23   *
24   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
27   * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
28   * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
29   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
30   * OTHER DEALINGS IN THE SOFTWARE.
31   * #L%
32   */
33  
34  import java.io.IOException;
35  import java.nio.Buffer;
36  import java.nio.ByteBuffer;
37  import java.nio.DoubleBuffer;
38  import java.nio.FloatBuffer;
39  import java.nio.IntBuffer;
40  import java.nio.LongBuffer;
41  import java.nio.ShortBuffer;
42  import java.util.zip.GZIPInputStream;
43  import java.util.zip.GZIPOutputStream;
44  
45  import nom.tam.fits.compression.algorithm.gzip.GZipCompressor;
46  import nom.tam.util.type.ElementType;
47  
48  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
49  
50  /**
51   * (<i>for internal use</i>) The GZIP2 compression algorithm.
52   *
53   * @param <T> The genetic type of element buffer to compress
54   */
55  @SuppressWarnings("javadoc")
56  public abstract class GZip2Compressor<T extends Buffer> extends GZipCompressor<T> {
57  
58      public static class ByteGZip2Compressor extends ByteGZipCompressor {
59      }
60  
61      public static class IntGZip2Compressor extends GZip2Compressor<IntBuffer> {
62  
63          public IntGZip2Compressor() {
64              super(ElementType.INT.size());
65          }
66  
67          @Override
68          protected void getPixel(IntBuffer pixelData, byte[] pixelBytes) {
69              IntBuffer pixelBuffer = ByteBuffer.wrap(pixelBytes).asIntBuffer();
70              pixelBuffer.put(pixelData);
71          }
72  
73          @Override
74          protected void setPixel(IntBuffer pixelData, byte[] pixelBytes) {
75              pixelData.put(ByteBuffer.wrap(pixelBytes).asIntBuffer());
76          }
77      }
78  
79      public static class FloatGZip2Compressor extends GZip2Compressor<FloatBuffer> {
80  
81          public FloatGZip2Compressor() {
82              super(ElementType.FLOAT.size());
83          }
84  
85          @Override
86          protected void getPixel(FloatBuffer pixelData, byte[] pixelBytes) {
87              FloatBuffer pixelBuffer = ByteBuffer.wrap(pixelBytes).asFloatBuffer();
88              pixelBuffer.put(pixelData);
89          }
90  
91          @Override
92          protected void setPixel(FloatBuffer pixelData, byte[] pixelBytes) {
93              pixelData.put(ByteBuffer.wrap(pixelBytes).asFloatBuffer());
94          }
95      }
96  
97      public static class LongGZip2Compressor extends GZip2Compressor<LongBuffer> {
98  
99          public LongGZip2Compressor() {
100             super(ElementType.LONG.size());
101         }
102 
103         @Override
104         protected void getPixel(LongBuffer pixelData, byte[] pixelBytes) {
105             LongBuffer pixelBuffer = ByteBuffer.wrap(pixelBytes).asLongBuffer();
106             pixelBuffer.put(pixelData);
107         }
108 
109         @Override
110         protected void setPixel(LongBuffer pixelData, byte[] pixelBytes) {
111             pixelData.put(ByteBuffer.wrap(pixelBytes).asLongBuffer());
112         }
113     }
114 
115     public static class DoubleGZip2Compressor extends GZip2Compressor<DoubleBuffer> {
116 
117         public DoubleGZip2Compressor() {
118             super(ElementType.DOUBLE.size());
119         }
120 
121         @Override
122         protected void getPixel(DoubleBuffer pixelData, byte[] pixelBytes) {
123             DoubleBuffer pixelBuffer = ByteBuffer.wrap(pixelBytes).asDoubleBuffer();
124             pixelBuffer.put(pixelData);
125         }
126 
127         @Override
128         protected void setPixel(DoubleBuffer pixelData, byte[] pixelBytes) {
129             pixelData.put(ByteBuffer.wrap(pixelBytes).asDoubleBuffer());
130         }
131     }
132 
133     public static class ShortGZip2Compressor extends GZip2Compressor<ShortBuffer> {
134 
135         public ShortGZip2Compressor() {
136             super(ElementType.SHORT.size());
137         }
138 
139         @Override
140         protected void getPixel(ShortBuffer pixelData, byte[] pixelBytes) {
141             ShortBuffer shortBuffer = ByteBuffer.wrap(pixelBytes).asShortBuffer();
142             shortBuffer.put(pixelData);
143         }
144 
145         @Override
146         protected void setPixel(ShortBuffer pixelData, byte[] pixelBytes) {
147             pixelData.put(ByteBuffer.wrap(pixelBytes).asShortBuffer());
148         }
149     }
150 
151     public GZip2Compressor(int primitiveSize) {
152         super(primitiveSize);
153     }
154 
155     private int[] calculateOffsets(byte[] byteArray) {
156         int[] offset = new int[primitiveSize];
157         offset[0] = 0;
158         for (int primitivIndex = 1; primitivIndex < primitiveSize; primitivIndex++) {
159             offset[primitivIndex] = offset[primitivIndex - 1] + byteArray.length / primitiveSize;
160         }
161         return offset;
162     }
163 
164     @Override
165     @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE", justification = "what null check is FB even referring to?")
166     public boolean compress(T pixelData, ByteBuffer compressed) {
167         int pixelDataLimit = pixelData.limit();
168         byte[] pixelBytes = new byte[pixelDataLimit * primitiveSize];
169         getPixel(pixelData, pixelBytes);
170         pixelBytes = shuffle(pixelBytes);
171         try (GZIPOutputStream zip = createGZipOutputStream(pixelDataLimit, compressed)) {
172             // FIXME AK: FB complains the line below has a redundant null ckeck for 'zip', but where exactly?
173             zip.write(pixelBytes, 0, pixelBytes.length);
174         } catch (IOException e) {
175             throw new IllegalStateException("could not gzip data", e);
176         }
177         return true;
178     }
179 
180     @Override
181     public void decompress(ByteBuffer compressed, T pixelData) {
182         int pixelDataLimit = pixelData.limit();
183         byte[] pixelBytes = new byte[pixelDataLimit * primitiveSize];
184         try (GZIPInputStream zip = createGZipInputStream(compressed)) {
185             int count = 0;
186             int offset = 0;
187             while (offset < pixelBytes.length && count >= 0) {
188                 count = zip.read(pixelBytes, offset, pixelBytes.length - offset);
189                 if (count >= 0) {
190                     offset = offset + count;
191                 }
192             }
193         } catch (IOException e) {
194             throw new IllegalStateException("could not gunzip data", e);
195         }
196         pixelBytes = unshuffle(pixelBytes);
197         setPixel(pixelData, pixelBytes);
198     }
199 
200     public byte[] shuffle(byte[] byteArray) {
201         byte[] result = new byte[byteArray.length];
202         int resultIndex = 0;
203         int[] offset = calculateOffsets(byteArray);
204         for (int index = 0; index < byteArray.length; index += primitiveSize) {
205             for (int primitiveIndex = 0; primitiveIndex < primitiveSize; primitiveIndex++) {
206                 result[resultIndex + offset[primitiveIndex]] = byteArray[index + primitiveIndex];
207             }
208             resultIndex++;
209         }
210         return result;
211     }
212 
213     public byte[] unshuffle(byte[] byteArray) {
214         byte[] result = new byte[byteArray.length];
215         int resultIndex = 0;
216         int[] offset = calculateOffsets(byteArray);
217         for (int index = 0; index < byteArray.length; index += primitiveSize) {
218             for (int primitiveIndex = 0; primitiveIndex < primitiveSize; primitiveIndex++) {
219                 result[index + primitiveIndex] = byteArray[resultIndex + offset[primitiveIndex]];
220             }
221             resultIndex++;
222         }
223         return result;
224     }
225 }