diff --git a/src/main/java/cz/it4i/qcmp/cache/ICacheFile.java b/src/main/java/cz/it4i/qcmp/cache/ICacheFile.java index ac77259775965e2ece828e0e9bad9b9323586edc..555175588b53b52744e09f1be636a92ec3803adb 100644 --- a/src/main/java/cz/it4i/qcmp/cache/ICacheFile.java +++ b/src/main/java/cz/it4i/qcmp/cache/ICacheFile.java @@ -1,6 +1,6 @@ package cz.it4i.qcmp.cache; -import cz.it4i.qcmp.fileformat.CacheFileHeader; +import cz.it4i.qcmp.fileformat.cache.CacheFileHeaderV1; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -12,9 +12,9 @@ public interface ICacheFile { void readFromStream(DataInputStream inputStream) throws IOException; - void readFromStream(DataInputStream inputStream, CacheFileHeader header) throws IOException; + void readFromStream(DataInputStream inputStream, CacheFileHeaderV1 header) throws IOException; - CacheFileHeader getHeader(); + CacheFileHeaderV1 getHeader(); void report(StringBuilder builder); diff --git a/src/main/java/cz/it4i/qcmp/cache/QuantizationCacheManager.java b/src/main/java/cz/it4i/qcmp/cache/QuantizationCacheManager.java index 8903a91635b239149e85168e943074b6889555ee..2cb096855733b517823834102416721a45dd94b5 100644 --- a/src/main/java/cz/it4i/qcmp/cache/QuantizationCacheManager.java +++ b/src/main/java/cz/it4i/qcmp/cache/QuantizationCacheManager.java @@ -2,8 +2,8 @@ package cz.it4i.qcmp.cache; import cz.it4i.qcmp.compression.CompressionOptions; import cz.it4i.qcmp.data.V3i; -import cz.it4i.qcmp.fileformat.CacheFileHeader; import cz.it4i.qcmp.fileformat.QuantizationType; +import cz.it4i.qcmp.fileformat.cache.CacheFileHeaderV1; import cz.it4i.qcmp.quantization.scalar.SQCodebook; import cz.it4i.qcmp.quantization.vector.VQCodebook; @@ -68,8 +68,8 @@ public class QuantizationCacheManager { * @param codebook Final SQ codebook. * @return SQ cache file header. */ - private CacheFileHeader createHeaderForSQ(final String trainFile, final SQCodebook codebook) { - final CacheFileHeader header = new CacheFileHeader(); + private CacheFileHeaderV1 createHeaderForSQ(final String trainFile, final SQCodebook codebook) { + final CacheFileHeaderV1 header = new CacheFileHeaderV1(); header.setQuantizationType(QuantizationType.Scalar); header.setCodebookSize(codebook.getCodebookSize()); header.setTrainFileName(trainFile); @@ -105,8 +105,8 @@ public class QuantizationCacheManager { * @param codebook Final VQ codebook. * @return VQ cache file header. */ - private CacheFileHeader createHeaderForVQ(final String trainFile, final VQCodebook codebook) { - final CacheFileHeader header = new CacheFileHeader(); + private CacheFileHeaderV1 createHeaderForVQ(final String trainFile, final VQCodebook codebook) { + final CacheFileHeaderV1 header = new CacheFileHeaderV1(); header.setQuantizationType(getQuantizationTypeFromVectorDimensions(codebook.getVectorDims())); header.setCodebookSize(codebook.getCodebookSize()); header.setTrainFileName(trainFile); @@ -125,7 +125,7 @@ public class QuantizationCacheManager { public String saveCodebook(final String trainFile, final SQCodebook codebook) throws IOException { final String fileName = getCacheFilePathForSQ(trainFile, codebook.getCodebookSize()).getAbsolutePath(); - final CacheFileHeader header = createHeaderForSQ(new File(trainFile).getName(), codebook); + final CacheFileHeaderV1 header = createHeaderForSQ(new File(trainFile).getName(), codebook); final SQCacheFile cacheFile = new SQCacheFile(header, codebook); try (final FileOutputStream fos = new FileOutputStream(fileName, false); @@ -152,7 +152,7 @@ public class QuantizationCacheManager { codebook.getCodebookSize(), codebook.getVectorDims()).getAbsolutePath(); - final CacheFileHeader header = createHeaderForVQ(new File(trainFile).getName(), codebook); + final CacheFileHeaderV1 header = createHeaderForVQ(new File(trainFile).getName(), codebook); final VQCacheFile cacheFile = new VQCacheFile(header, codebook); try (final FileOutputStream fos = new FileOutputStream(fileName, false); @@ -357,7 +357,7 @@ public class QuantizationCacheManager { dis = new DataInputStream(inputStream); } - final CacheFileHeader header = new CacheFileHeader(); + final CacheFileHeaderV1 header = new CacheFileHeaderV1(); try { header.readFromStream(dis); } catch (final IOException e) { @@ -409,12 +409,12 @@ public class QuantizationCacheManager { * @param path Path to cache file. */ public static void inspectCacheFile(final String path, final boolean verbose) { - CacheFileHeader header = null; + CacheFileHeaderV1 header = null; final long fileSize; try (final FileInputStream fis = new FileInputStream(path); final DataInputStream dis = new DataInputStream(fis)) { fileSize = fis.getChannel().size(); - header = new CacheFileHeader(); + header = new CacheFileHeaderV1(); header.readFromStream(dis); } catch (final IOException e) { e.printStackTrace(); diff --git a/src/main/java/cz/it4i/qcmp/cache/SQCacheFile.java b/src/main/java/cz/it4i/qcmp/cache/SQCacheFile.java index b16e402af4038766bdec5d9e4582bf9f37c7c356..1847f5e75d6d6c805d335170544269dbbf91e8cc 100644 --- a/src/main/java/cz/it4i/qcmp/cache/SQCacheFile.java +++ b/src/main/java/cz/it4i/qcmp/cache/SQCacheFile.java @@ -1,6 +1,6 @@ package cz.it4i.qcmp.cache; -import cz.it4i.qcmp.fileformat.CacheFileHeader; +import cz.it4i.qcmp.fileformat.cache.CacheFileHeaderV1; import cz.it4i.qcmp.quantization.scalar.SQCodebook; import java.io.DataInputStream; @@ -8,13 +8,13 @@ import java.io.DataOutputStream; import java.io.IOException; public class SQCacheFile implements ICacheFile { - private CacheFileHeader header; + private CacheFileHeaderV1 header; private SQCodebook codebook; public SQCacheFile() { } - public SQCacheFile(final CacheFileHeader header, final SQCodebook codebook) { + public SQCacheFile(final CacheFileHeaderV1 header, final SQCodebook codebook) { this.header = header; this.codebook = codebook; assert (header.getCodebookSize() == codebook.getCodebookSize()); @@ -34,12 +34,12 @@ public class SQCacheFile implements ICacheFile { } public void readFromStream(final DataInputStream inputStream) throws IOException { - header = new CacheFileHeader(); + header = new CacheFileHeaderV1(); header.readFromStream(inputStream); readFromStream(inputStream, header); } - public void readFromStream(final DataInputStream inputStream, final CacheFileHeader header) throws IOException { + public void readFromStream(final DataInputStream inputStream, final CacheFileHeaderV1 header) throws IOException { this.header = header; final int codebookSize = header.getCodebookSize(); final int[] centroids = new int[codebookSize]; @@ -54,7 +54,7 @@ public class SQCacheFile implements ICacheFile { codebook = new SQCodebook(centroids, frequencies); } - public CacheFileHeader getHeader() { + public CacheFileHeaderV1 getHeader() { return header; } diff --git a/src/main/java/cz/it4i/qcmp/cache/VQCacheFile.java b/src/main/java/cz/it4i/qcmp/cache/VQCacheFile.java index 8f4414b178704df91991a96f5cec5e204dbae1bd..6f062a5e13d9d234cf80a61542e8071f166c4241 100644 --- a/src/main/java/cz/it4i/qcmp/cache/VQCacheFile.java +++ b/src/main/java/cz/it4i/qcmp/cache/VQCacheFile.java @@ -1,6 +1,6 @@ package cz.it4i.qcmp.cache; -import cz.it4i.qcmp.fileformat.CacheFileHeader; +import cz.it4i.qcmp.fileformat.cache.CacheFileHeaderV1; import cz.it4i.qcmp.quantization.vector.VQCodebook; import java.io.DataInputStream; @@ -8,13 +8,13 @@ import java.io.DataOutputStream; import java.io.IOException; public class VQCacheFile implements ICacheFile { - private CacheFileHeader header; + private CacheFileHeaderV1 header; private VQCodebook codebook; public VQCacheFile() { } - public VQCacheFile(final CacheFileHeader header, final VQCodebook codebook) { + public VQCacheFile(final CacheFileHeaderV1 header, final VQCodebook codebook) { this.header = header; this.codebook = codebook; assert (header.getCodebookSize() == codebook.getCodebookSize()); @@ -37,13 +37,13 @@ public class VQCacheFile implements ICacheFile { } public void readFromStream(final DataInputStream inputStream) throws IOException { - header = new CacheFileHeader(); + header = new CacheFileHeaderV1(); header.readFromStream(inputStream); readFromStream(inputStream, header); } @Override - public void readFromStream(final DataInputStream inputStream, final CacheFileHeader header) throws IOException { + public void readFromStream(final DataInputStream inputStream, final CacheFileHeaderV1 header) throws IOException { this.header = header; final int codebookSize = header.getCodebookSize(); @@ -64,7 +64,7 @@ public class VQCacheFile implements ICacheFile { codebook = new VQCodebook(header.getVectorDim(), vectors, frequencies); } - public CacheFileHeader getHeader() { + public CacheFileHeaderV1 getHeader() { return header; } diff --git a/src/main/java/cz/it4i/qcmp/fileformat/IFileHeader.java b/src/main/java/cz/it4i/qcmp/fileformat/IFileHeader.java new file mode 100644 index 0000000000000000000000000000000000000000..5fc5cb2f746120de71bdb662e6dd699a0cd8540a --- /dev/null +++ b/src/main/java/cz/it4i/qcmp/fileformat/IFileHeader.java @@ -0,0 +1,21 @@ +package cz.it4i.qcmp.fileformat; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public interface IFileHeader { + String getMagicValue(); + + int getHeaderVersion(); + + boolean validateHeader(); + + void writeToStream(final DataOutputStream stream) throws IOException; + + void readFromStream(final DataInputStream stream) throws IOException; + + void report(final StringBuilder builder); + + long getExpectedFileSize(); +} diff --git a/src/main/java/cz/it4i/qcmp/fileformat/CacheFileHeader.java b/src/main/java/cz/it4i/qcmp/fileformat/cache/CacheFileHeaderV1.java similarity index 78% rename from src/main/java/cz/it4i/qcmp/fileformat/CacheFileHeader.java rename to src/main/java/cz/it4i/qcmp/fileformat/cache/CacheFileHeaderV1.java index ef775618ab74af0657f0ba76ace2beb1c0d091c4..4538418ac61f48d576574ee40eb9ae18d55277ac 100644 --- a/src/main/java/cz/it4i/qcmp/fileformat/CacheFileHeader.java +++ b/src/main/java/cz/it4i/qcmp/fileformat/cache/CacheFileHeaderV1.java @@ -1,87 +1,81 @@ -package cz.it4i.qcmp.fileformat; +package cz.it4i.qcmp.fileformat.cache; import cz.it4i.qcmp.data.V2i; import cz.it4i.qcmp.data.V3i; +import cz.it4i.qcmp.fileformat.IFileHeader; +import cz.it4i.qcmp.fileformat.QuantizationType; +import cz.it4i.qcmp.io.RawDataIO; import cz.it4i.qcmp.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"; +public class CacheFileHeaderV1 implements IFileHeader { + //region Constants + private static final int VERSION = 1; + private static final String MAGIC_VALUE = "QCMPCACHE"; + //endregion + + //region Header fields. private String magicValue; private QuantizationType quantizationType; - private int codebookSize; - private int trainFileNameSize; private String trainFileName; - private int vectorSizeX; private int vectorSizeY; private int vectorSizeZ; + //endregion - public void setQuantizationType(final QuantizationType quantizationType) { - this.quantizationType = quantizationType; + //region IFileHeader implementation + @Override + public String getMagicValue() { + return MAGIC_VALUE; } - public void setCodebookSize(final int codebookSize) { - this.codebookSize = codebookSize; + @Override + public int getHeaderVersion() { + return VERSION; } - - public void setTrainFileName(final String trainFileName) { - this.trainFileName = trainFileName; - this.trainFileNameSize = this.trainFileName.length(); + @Override + public boolean validateHeader() { + return (magicValue != null && magicValue.equals(MAGIC_VALUE)); } - 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; - } + /** + * Read header from the stream. + * + * @param inputStream Data input stream. + */ + @Override + public void readFromStream(final DataInputStream inputStream) throws IOException { + final int MIN_AVAIL = 9; + if (inputStream.available() < MIN_AVAIL) { + throw new IOException("Provided file is not QCMP cache file."); + } - public int getBitsPerCodebookIndex() { - return (int) Utils.log2(codebookSize); - } + final byte[] magicValueBuffer = new byte[MAGIC_VALUE.length()]; + RawDataIO.readFullBuffer(inputStream, magicValueBuffer); - public int getTrainFileNameSize() { - return trainFileNameSize; - } - - public String getTrainFileName() { - return trainFileName; - } + magicValue = new String(magicValueBuffer); + if (!magicValue.equals(MAGIC_VALUE)) { + throw new IOException("Invalid QCMP cache file. Wrong magic value."); + } + quantizationType = QuantizationType.fromByte(inputStream.readByte()); + codebookSize = inputStream.readUnsignedShort(); - public int getVectorSizeX() { - return vectorSizeX; - } + trainFileNameSize = inputStream.readUnsignedShort(); - public int getVectorSizeY() { - return vectorSizeY; - } + final byte[] fileNameBuffer = new byte[trainFileNameSize]; + RawDataIO.readFullBuffer(inputStream, fileNameBuffer); - public int getVectorSizeZ() { - return vectorSizeZ; - } + trainFileName = new String(fileNameBuffer); - public V3i getVectorDim() { - return new V3i(vectorSizeX, vectorSizeY, vectorSizeZ); + vectorSizeX = inputStream.readUnsignedShort(); + vectorSizeY = inputStream.readUnsignedShort(); + vectorSizeZ = inputStream.readUnsignedShort(); } /** @@ -90,8 +84,9 @@ public class CacheFileHeader { * @param outputStream Data output stream. * @throws IOException when fails to write the header to stream. */ + @Override public void writeToStream(final DataOutputStream outputStream) throws IOException { - outputStream.writeBytes(QCMP_CACHE_MAGIC_VALUE); + outputStream.writeBytes(MAGIC_VALUE); outputStream.writeByte(quantizationType.getValue()); outputStream.writeShort(codebookSize); @@ -103,6 +98,7 @@ public class CacheFileHeader { outputStream.writeShort(vectorSizeZ); } + @Override public long getExpectedFileSize() { long expectedFileSize = 20 + trainFileNameSize; // Base header size expectedFileSize += (codebookSize * 8); // Frequency values @@ -121,59 +117,9 @@ public class CacheFileHeader { 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(); - } - + @Override public void report(final StringBuilder sb) { + sb.append("HeaderVersion: ").append(VERSION).append('\n'); sb.append("Magic: ").append(magicValue).append('\n'); sb.append("CodebookType: "); @@ -194,4 +140,77 @@ public class CacheFileHeader { sb.append("CodebookSize: ").append(codebookSize).append('\n'); sb.append("TrainFile: ").append(trainFileName).append('\n'); } + //endregion + + //region Getters and Setters + 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); + } + + + public void setVectorDims(final V3i v3i) { + this.vectorSizeX = v3i.getX(); + this.vectorSizeY = v3i.getY(); + this.vectorSizeZ = v3i.getZ(); + } + + //endregion } diff --git a/src/main/java/cz/it4i/qcmp/io/RawDataIO.java b/src/main/java/cz/it4i/qcmp/io/RawDataIO.java index d1af8b3ea50310f4579877025ce21f4e4fa4517f..6a16d014ebdd1dbd246300cfa9e37b246ef697b0 100644 --- a/src/main/java/cz/it4i/qcmp/io/RawDataIO.java +++ b/src/main/java/cz/it4i/qcmp/io/RawDataIO.java @@ -5,6 +5,7 @@ import cz.it4i.qcmp.utilities.TypeConverter; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; public class RawDataIO { @@ -30,6 +31,24 @@ public class RawDataIO { fileStream.close(); } + /** + * Read exactly N bytes from stream into the buffer. + * + * @param stream Input stream. + * @param buffer Memory buffer. + * @throws IOException when unable to read buffer.length bytes. + */ + public static void readFullBuffer(final InputStream stream, final byte[] buffer) throws IOException { + int toRead = buffer.length; + while (toRead > 0) { + final int read = stream.read(buffer, (buffer.length - toRead), toRead); + if (read < 0) { + throw new IOException("Unable to read requested number of bytes."); + } + toRead -= read; + } + } + public static void write(final String outFile, final int[] sliceData, final boolean littleEndian) throws IOException { writeBytesToFile(outFile, TypeConverter.unsignedShortArrayToByteArray(sliceData, littleEndian));