1 package nom.tam.image.compression.bintable;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 import java.io.IOException;
35 import java.nio.Buffer;
36 import java.nio.ByteBuffer;
37 import java.util.Arrays;
38
39 import nom.tam.fits.BinaryTable;
40 import nom.tam.fits.compression.algorithm.api.ICompressorControl;
41 import nom.tam.image.compression.hdu.CompressedTableData;
42 import nom.tam.util.ArrayOutputStream;
43 import nom.tam.util.ByteBufferOutputStream;
44 import nom.tam.util.ColumnTable;
45 import nom.tam.util.FitsOutputStream;
46 import nom.tam.util.type.ElementType;
47
48
49
50
51 @SuppressWarnings("javadoc")
52 public class BinaryTableTileCompressor extends BinaryTableTile {
53
54 private static final double NORMAL_OVERHEAD = 1.2;
55
56 private static final int MINIMUM_EXTRA_SPACE = 1024;
57
58 private final CompressedTableData compressed;
59
60
61 private BinaryTable orig;
62
63
64 private byte[][] compressedBytes;
65 private long[][] cdesc;
66 private Object udesc;
67
68
69
70
71
72
73 @Deprecated
74 public BinaryTableTileCompressor(CompressedTableData compressedTable, ColumnTable<?> columnTable,
75 BinaryTableTileDescription description) {
76 super(columnTable, description);
77 this.compressed = compressedTable;
78 }
79
80
81
82
83
84
85
86
87 public BinaryTableTileCompressor(CompressedTableData compressedTable, BinaryTable table,
88 BinaryTableTileDescription description) {
89 this(compressedTable, table.getData(), description);
90 this.orig = table;
91 }
92
93 private int getCushion(int size, double factor) throws IllegalStateException {
94 long lsize = (long) Math.ceil(size * factor + MINIMUM_EXTRA_SPACE);
95 return (lsize > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) lsize;
96 }
97
98 private byte[] getCompressedBytes(ByteBuffer buffer, ElementType<?> t, ICompressorControl compressor) {
99 buffer.flip();
100
101
102 int need = getCushion(getUncompressedSizeInBytes(), NORMAL_OVERHEAD);
103 ByteBuffer cbuf = ByteBuffer.allocateDirect(need);
104
105 Buffer tb = t.asTypedBuffer(buffer);
106
107 if (!compressor.compress(tb, cbuf, null)) {
108 throw new IllegalStateException("Compression error");
109 }
110
111 cbuf.flip();
112 byte[] cdata = new byte[cbuf.limit()];
113 cbuf.get(cdata);
114
115 buffer.clear();
116
117 return cdata;
118 }
119
120 private void compressRegular() throws IOException {
121 compressedBytes = new byte[1][];
122
123 ByteBuffer buffer = ByteBuffer.allocateDirect(getUncompressedSizeInBytes());
124 try (FitsOutputStream os = new FitsOutputStream(new ByteBufferOutputStream(buffer))) {
125 data.write(os, rowStart, rowEnd, column);
126 }
127
128 compressedBytes[0] = getCompressedBytes(buffer, type, getCompressorControl());
129 }
130
131 private void compressVariable() throws IOException {
132 int nRows = rowEnd - rowStart;
133 boolean longPointers = orig.getDescriptor(column).hasLongPointers();
134 long max = 0;
135
136 udesc = longPointers ? new long[nRows][] : new int[nRows][2];
137
138
139 for (int r = 0; r < nRows; r++) {
140 Object desc = data.getElement(rowStart + r, column);
141 long n = 0;
142
143 if (longPointers) {
144 ((long[][]) udesc)[r] = (long[]) desc;
145 n = ((long[]) desc)[0];
146 } else {
147 ((int[][]) udesc)[r] = (int[]) desc;
148 n = ((int[]) desc)[0];
149 }
150
151 if (n > max) {
152 max = n;
153 }
154 }
155
156 max *= type.size();
157
158
159 if (max > Integer.MAX_VALUE) {
160 throw new IllegalStateException("Uncompressed data too large for Java arrays: max=" + max);
161 }
162
163
164 ByteBuffer buffer = ByteBuffer.allocateDirect((int) max);
165 ElementType<?> dataType = ElementType.forClass(orig.getDescriptor(column).getElementClass());
166
167 ICompressorControl compressor = getCompressorControl(dataType.primitiveClass());
168 compressedBytes = new byte[nRows][];
169
170 for (int r = 0; r < nRows; r++) {
171 try (FitsOutputStream os = new FitsOutputStream(new ByteBufferOutputStream(buffer))) {
172
173 Object entry = orig.get(rowStart + r, column);
174 os.writeArray(entry);
175 }
176
177 compressedBytes[r] = getCompressedBytes(buffer, dataType, compressor);
178 }
179
180 }
181
182 @Override
183 public void run() {
184 try {
185 if (orig != null && orig.getDescriptor(column).isVariableSize()) {
186
187 compressVariable();
188 } else {
189
190 compressRegular();
191 }
192 } catch (IOException e) {
193 throw new IllegalStateException(e.getMessage(), e);
194 }
195 }
196
197 private Object setCompressedData(byte[] data) {
198 synchronized (compressed) {
199
200 Object p = compressed.getData().getElement(getTileIndex(), column);
201 if (p instanceof long[]) {
202 Arrays.fill((long[]) p, 0L);
203 } else {
204 Arrays.fill((int[]) p, 0);
205 }
206 compressed.getData().setElement(getTileIndex(), column, p);
207
208
209 compressed.setElement(getTileIndex(), column, data);
210
211
212
213 return compressed.getData().getElement(getTileIndex(), column);
214 }
215
216 }
217
218 private void setRegularData() {
219 setCompressedData(compressedBytes[0]);
220
221
222 compressedBytes = null;
223 }
224
225 private void setVariableData() throws IOException {
226 int nRows = compressedBytes.length;
227 boolean longPointers = orig.getDescriptor(column).hasLongPointers();
228
229 cdesc = new long[nRows][2];
230
231 ByteBuffer buffer = ByteBuffer
232 .allocateDirect((nRows * 2) * (Long.SIZE + (longPointers ? Long.BYTES : Integer.BYTES)));
233
234 for (int r = 0; r < nRows; r++) {
235
236 Object cp = setCompressedData(compressedBytes[r]);
237
238 if (cp instanceof long[]) {
239 cdesc[r] = (long[]) cp;
240 } else {
241
242 cdesc[r][0] = ((int[]) cp)[0];
243 cdesc[r][1] = ((int[]) cp)[1];
244 }
245 }
246
247 try (ArrayOutputStream os = new FitsOutputStream(new ByteBufferOutputStream(buffer))) {
248
249
250 os.writeArray(udesc);
251
252 os.writeArray(cdesc);
253 }
254
255
256
257 setCompressedData(getCompressedBytes(buffer, ElementType.BYTE, getGZipCompressorControl()));
258
259
260 compressedBytes = null;
261 cdesc = null;
262 udesc = null;
263 }
264
265 @Override
266 public void waitForResult() {
267 super.waitForResult();
268
269 if (orig != null && orig.getDescriptor(column).isVariableSize()) {
270 try {
271 setVariableData();
272 } catch (IOException e) {
273 throw new IllegalStateException(e.getMessage(), e);
274 }
275 } else {
276 setRegularData();
277 }
278
279 }
280
281 }