Skip to content
Snippets Groups Projects
CacheFileHeader.java 6.03 KiB
package azgracompress.cache;

import azgracompress.data.V2i;
import azgracompress.data.V3i;
import azgracompress.fileformat.QuantizationType;
import azgracompress.utilities.Utils;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

public class CacheFileHeader {
    public static final String QCMP_CACHE_MAGIC_VALUE = "QCMPCACHE";
    private String magicValue;
    private QuantizationType quantizationType;

    private int codebookSize;

    private int trainFileNameSize;
    private String trainFileName;

    private int vectorSizeX;
    private int vectorSizeY;
    private int vectorSizeZ;

    public void setQuantizationType(final QuantizationType quantizationType) {
        this.quantizationType = quantizationType;
    }

    public void setCodebookSize(final int codebookSize) {
        this.codebookSize = codebookSize;
    }


    public void setTrainFileName(final String trainFileName) {
        this.trainFileName = trainFileName;
        this.trainFileNameSize = this.trainFileName.length();
    }

    public void setVectorSizeX(final int vectorSizeX) {
        this.vectorSizeX = vectorSizeX;
    }

    public void setVectorSizeY(final int vectorSizeY) {
        this.vectorSizeY = vectorSizeY;
    }

    public void setVectorSizeZ(final int vectorSizeZ) {
        this.vectorSizeZ = vectorSizeZ;
    }

    public QuantizationType getQuantizationType() {
        return quantizationType;
    }

    public int getCodebookSize() {
        return codebookSize;
    }

    public int getBitsPerCodebookIndex() {
        return (int) Utils.log2(codebookSize);
    }

    public int getTrainFileNameSize() {
        return trainFileNameSize;
    }

    public String getTrainFileName() {
        return trainFileName;
    }

    public int getVectorSizeX() {
        return vectorSizeX;
    }

    public int getVectorSizeY() {
        return vectorSizeY;
    }

    public int getVectorSizeZ() {
        return vectorSizeZ;
    }

    public V3i getVectorDim() {
        return new V3i(vectorSizeX, vectorSizeY, vectorSizeZ);
    }

    /**
     * Write QCMP cache file header to stream.
     *
     * @param outputStream Data output stream.
     * @throws IOException when fails to write the header to stream.
     */
    public void writeToStream(final DataOutputStream outputStream) throws IOException {
        outputStream.writeBytes(QCMP_CACHE_MAGIC_VALUE);
        outputStream.writeByte(quantizationType.getValue());
        outputStream.writeShort(codebookSize);

        outputStream.writeShort(trainFileName.length());
        outputStream.writeBytes(trainFileName);

        outputStream.writeShort(vectorSizeX);
        outputStream.writeShort(vectorSizeY);
        outputStream.writeShort(vectorSizeZ);
    }

    public long getExpectedFileSize() {
        long expectedFileSize = 20 + trainFileNameSize; // Base header size
        expectedFileSize += (codebookSize * 8);         // Frequency values
        switch (quantizationType) {
            case Scalar:
                expectedFileSize += (codebookSize * 2); // Scalar quantization values
                break;
            case Vector1D:
            case Vector2D:
            case Vector3D:
                expectedFileSize += ((vectorSizeX * vectorSizeY * vectorSizeZ) * codebookSize * 2); // Quantization vectors
                break;
            case Invalid:
                return -1;
        }
        return expectedFileSize;
    }

    /**
     * Read header from the stream.
     *
     * @param inputStream Data input stream.
     */
    public void readFromStream(final DataInputStream inputStream) throws IOException {
        final int MIN_AVAIL = 9;
        if (inputStream.available() < MIN_AVAIL) {
            throw new IOException("Invalid file. File too small.");
        }

        final byte[] magicBuffer = new byte[QCMP_CACHE_MAGIC_VALUE.length()];

        int toRead = QCMP_CACHE_MAGIC_VALUE.length();
        while (toRead > 0) {
            final int read = inputStream.read(magicBuffer, QCMP_CACHE_MAGIC_VALUE.length() - toRead, toRead);
            if (read < 0) {
                throw new IOException("Invalid file type. Unable to read magic value");
            }
            toRead -= read;
        }

        magicValue = new String(magicBuffer);
        if (!magicValue.equals(QCMP_CACHE_MAGIC_VALUE)) {
            throw new IOException("Invalid file type. Wrong magic value.");
        }
        quantizationType = QuantizationType.fromByte(inputStream.readByte());
        codebookSize = inputStream.readUnsignedShort();

        trainFileNameSize = inputStream.readUnsignedShort();
        final byte[] fileNameBuffer = new byte[trainFileNameSize];


        toRead = trainFileNameSize;
        while (toRead > 0) {
            toRead -= inputStream.read(fileNameBuffer, trainFileNameSize - toRead, toRead);
        }


        trainFileName = new String(fileNameBuffer);

        vectorSizeX = inputStream.readUnsignedShort();
        vectorSizeY = inputStream.readUnsignedShort();
        vectorSizeZ = inputStream.readUnsignedShort();
    }

    public void setVectorDims(final V3i v3i) {
        this.vectorSizeX = v3i.getX();
        this.vectorSizeY = v3i.getY();
        this.vectorSizeZ = v3i.getZ();
    }

    public void report(final StringBuilder sb) {
        sb.append("Magic: ").append(magicValue).append('\n');

        sb.append("CodebookType: ");
        switch (quantizationType) {
            case Scalar:
                sb.append("Scalar\n");
                break;
            case Vector1D:
                sb.append(String.format("Vector1D [%sx1]\n", vectorSizeX));
                break;
            case Vector2D:
                sb.append(String.format("Vector2D %s\n", new V2i(vectorSizeX, vectorSizeY).toString()));
                break;
            case Vector3D:
                sb.append(String.format("Vector3D %s\n", new V3i(vectorSizeX, vectorSizeY, vectorSizeZ).toString()));
                break;
        }
        sb.append("CodebookSize: ").append(codebookSize).append('\n');
        sb.append("TrainFile: ").append(trainFileName).append('\n');
    }
}