All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nom.tam.image.compression.bintable.BinaryTableTileDecompressor Maven / Gradle / Ivy

Go to download

Java library for reading and writing FITS files. FITS, the Flexible Image Transport System, is the format commonly used in the archiving and transport of astronomical data.

There is a newer version: 1.21.0
Show newest version
package nom.tam.image.compression.bintable;

/*-
 * #%L
 * nom.tam.fits
 * %%
 * Copyright (C) 1996 - 2024 nom-tam-fits
 * %%
 * This is free and unencumbered software released into the public domain.
 * 
 * Anyone is free to copy, modify, publish, use, compile, sell, or
 * distribute this software, either in source code form or as a compiled
 * binary, for any purpose, commercial or non-commercial, and by any
 * means.
 * 
 * In jurisdictions that recognize copyright laws, the author or authors
 * of this software dedicate any and all copyright interest in the
 * software to the public domain. We make this dedication for the benefit
 * of the public at large and to the detriment of our heirs and
 * successors. We intend this dedication to be an overt act of
 * relinquishment in perpetuity of all present and future rights to this
 * software under copyright law.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 * #L%
 */

import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;

import nom.tam.fits.BinaryTable;
import nom.tam.fits.FitsException;
import nom.tam.fits.compression.algorithm.api.ICompressorControl;
import nom.tam.image.compression.hdu.CompressedTableData;
import nom.tam.image.compression.hdu.CompressedTableHDU;
import nom.tam.util.ByteBufferInputStream;
import nom.tam.util.ColumnTable;
import nom.tam.util.FitsInputStream;
import nom.tam.util.type.ElementType;

/**
 * (for internal use) Handles the decompression of binary table 'tiles'.
 */
@SuppressWarnings("javadoc")
public class BinaryTableTileDecompressor extends BinaryTableTile {

    private BinaryTable orig;

    private final CompressedTableData compressed;

    private int targetColumn = column;

    /**
     * @deprecated (for internal use) The visibility will be reduced in the future, not to mention that it should
     *                 take a binary table as its argument, with heap and all. It cannot be used for decompressing
     *                 binary tables with variable-length columns.
     */
    public BinaryTableTileDecompressor(CompressedTableData compressedTable, ColumnTable columnTable,
            BinaryTableTileDescription description) throws FitsException {
        super(columnTable, description);
        compressed = compressedTable;
    }

    public BinaryTableTileDecompressor(CompressedTableData compressedTable, BinaryTable table,
            BinaryTableTileDescription description) throws FitsException {
        this(compressedTable, table.getData(), description);
        orig = table;
    }

    private void decompressVariable() throws IOException {
        int nRows = rowEnd - rowStart;
        boolean longPointers = orig.getDescriptor(targetColumn).hasLongPointers();

        // Uncompress the adjoint heap pointer data stored in the compressed table using GZIP_1
        ByteBuffer pdata = ByteBuffer.wrap((byte[]) compressed.getElement(getTileIndex(), column));
        ByteBuffer pointers = ByteBuffer
                .allocateDirect((2 * nRows) * (Long.BYTES + (longPointers ? Long.BYTES : Integer.BYTES)));

        getGZipCompressorControl().decompress(pdata, pointers, null);
        pointers.flip();

        long[][] cdesc = new long[nRows][2];
        Object p = longPointers ? new long[nRows][2] : new int[nRows][2];

        try (FitsInputStream ips = new FitsInputStream(new ByteBufferInputStream(pointers))) {
            if (CompressedTableHDU.hasOldStandardVLAIndexing()) {
                // --- The FITS standard way ---
                // Restore the heap pointers to the compressed data in the compressed heap
                ips.readLArray(cdesc);
                // Restore the heap pointers for the original uncompressed data locations
                ips.readLArray(p);
            } else {
                // --- The fpack / funpack way ---
                // Restore the heap pointers for the original uncompressed data locations
                ips.readLArray(p);
                // Restore the heap pointers to the compressed data in the compressed heap
                ips.readLArray(cdesc);
            }
        }

        ElementType dataType = ElementType.forClass(orig.getDescriptor(column).getElementClass());

        ICompressorControl compressor = getCompressorControl(dataType.primitiveClass());

        // Save the original pointers for the compressed tile
        final Object bak = compressed.getData().getElement(getTileIndex(), column);

        try {
            for (int r = 0; r < nRows; r++) {
                long csize = cdesc[r][0];
                long coffset = cdesc[r][1];

                if (csize < 0 || csize > Integer.MAX_VALUE || coffset < 0 || coffset > Integer.MAX_VALUE) {
                    throw new FitsException(
                            "Illegal or unsupported compressed heap pointer (offset=" + coffset + ", size=" + csize);
                }

                long dcount = longPointers ? ((long[][]) p)[r][0] : ((int[][]) p)[r][0];
                long doffset = longPointers ? ((long[][]) p)[r][1] : ((int[][]) p)[r][1];

                if (dcount < 0 || dcount > Integer.MAX_VALUE || doffset < 0 || doffset > Integer.MAX_VALUE) {
                    throw new FitsException(
                            "Illegal or unsupported uncompressed heap pointer (offset=" + doffset + ", size=" + dcount);
                }

                // Temporarily replace the heap pointers in the compressed table with the pointers to the compressed row
                // entry
                Object temp = bak instanceof long[] ? new long[] {csize, coffset} : new int[] {(int) csize, (int) coffset};
                compressed.getData().setElement(getTileIndex(), column, temp);

                // Decompress the row entry, and write it to its original location on the heap
                ByteBuffer zip = ByteBuffer.wrap((byte[]) compressed.getElement(getTileIndex(), column));
                Buffer buf = dataType.newBuffer(dcount);
                compressor.decompress(zip, buf, null);
                buf.flip();

                // Restore the heap pointer in the uncompressed table
                data.setElement(rowStart + r, targetColumn, longPointers ? ((long[][]) p)[r] : ((int[][]) p)[r]);

                // Restore the uncompressed entry in the original heap location
                orig.setElement(rowStart + r, targetColumn, buf.array());
            }
        } finally {
            // Restore the original pointers for the compressed tile.
            compressed.getData().setElement(getTileIndex(), column, bak);
        }
    }

    private void decompressTableTile() throws IOException {
        ByteBuffer zip = ByteBuffer.wrap((byte[]) compressed.getElement(getTileIndex(), column));
        ByteBuffer buf = ByteBuffer.allocateDirect(getUncompressedSizeInBytes());

        getCompressorControl().decompress(zip, type.asTypedBuffer(buf), null);
        buf.rewind();

        try (FitsInputStream is = new FitsInputStream(new ByteBufferInputStream(buf))) {
            data.read(is, rowStart, rowEnd, targetColumn);
        }
    }

    @Override
    public void run() {
        try {
            if (orig != null && orig.getDescriptor(targetColumn).isVariableSize()) {
                // binary table with variable sized column
                decompressVariable();
            } else {
                // regular column table (fixed width columns)
                decompressTableTile();
            }
        } catch (IOException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    /**
     * Changes the comlumn index of into which the tile gets decompressed in the uncompressed table. By default the
     * decompressed column index will match the compressed data column index, which is great if we decompress the all
     * columns. However, we might decompress only selected table columns into a different table in which the column
     * indices are different.
     * 
     * @param  col the decompressed column index for the tile
     * 
     * @return     itself.
     * 
     * @since      1.18
     */
    public BinaryTableTileDecompressor decompressToColumn(int col) {
        targetColumn = col;
        return this;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy