diff --git a/src/main/java/azgracompress/cache/CacheFileHeader.java b/src/main/java/azgracompress/cache/CacheFileHeader.java index 0e6b5c1d1e02e582f69cd06a3cd8d527e9909751..d55ef41e79a6d208c05f31013548e16dc9cc4464 100644 --- a/src/main/java/azgracompress/cache/CacheFileHeader.java +++ b/src/main/java/azgracompress/cache/CacheFileHeader.java @@ -3,6 +3,7 @@ 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; @@ -22,29 +23,29 @@ public class CacheFileHeader { private int vectorSizeY; private int vectorSizeZ; - public void setQuantizationType(QuantizationType quantizationType) { + public void setQuantizationType(final QuantizationType quantizationType) { this.quantizationType = quantizationType; } - public void setCodebookSize(int codebookSize) { + public void setCodebookSize(final int codebookSize) { this.codebookSize = codebookSize; } - public void setTrainFileName(String trainFileName) { + public void setTrainFileName(final String trainFileName) { this.trainFileName = trainFileName; this.trainFileNameSize = this.trainFileName.length(); } - public void setVectorSizeX(int vectorSizeX) { + public void setVectorSizeX(final int vectorSizeX) { this.vectorSizeX = vectorSizeX; } - public void setVectorSizeY(int vectorSizeY) { + public void setVectorSizeY(final int vectorSizeY) { this.vectorSizeY = vectorSizeY; } - public void setVectorSizeZ(int vectorSizeZ) { + public void setVectorSizeZ(final int vectorSizeZ) { this.vectorSizeZ = vectorSizeZ; } @@ -56,6 +57,10 @@ public class CacheFileHeader { return codebookSize; } + public int getBitsPerCodebookIndex() { + return (int) Utils.log2(codebookSize); + } + public int getTrainFileNameSize() { return trainFileNameSize; } @@ -86,7 +91,7 @@ public class CacheFileHeader { * @param outputStream Data output stream. * @throws IOException when fails to write the header to stream. */ - public void writeToStream(DataOutputStream outputStream) throws IOException { + public void writeToStream(final DataOutputStream outputStream) throws IOException { outputStream.writeBytes(QCMP_CACHE_MAGIC_VALUE); outputStream.writeByte(quantizationType.getValue()); outputStream.writeShort(codebookSize); @@ -122,18 +127,23 @@ public class CacheFileHeader { * * @param inputStream Data input stream. */ - public void readFromStream(DataInputStream inputStream) throws IOException { + 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."); } - byte[] magicBuffer = new byte[QCMP_CACHE_MAGIC_VALUE.length()]; + final byte[] magicBuffer = new byte[QCMP_CACHE_MAGIC_VALUE.length()]; - final int readFromMagic = inputStream.read(magicBuffer, 0, QCMP_CACHE_MAGIC_VALUE.length()); - if (readFromMagic != QCMP_CACHE_MAGIC_VALUE.length()) { - throw new IOException("Invalid file type. Unable to read magic value"); + 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."); @@ -142,8 +152,15 @@ public class CacheFileHeader { codebookSize = inputStream.readUnsignedShort(); trainFileNameSize = inputStream.readUnsignedShort(); - byte[] fileNameBuffer = new byte[trainFileNameSize]; - final int readBytes = inputStream.read(fileNameBuffer, 0, trainFileNameSize); + 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(); diff --git a/src/main/java/azgracompress/compression/ImageCompressor.java b/src/main/java/azgracompress/compression/ImageCompressor.java index 4c23d1db16774067e1a9f1ddca6f1a1af8919056..2ace74cb14d8e7b01135d26f64df0b715e1ed568 100644 --- a/src/main/java/azgracompress/compression/ImageCompressor.java +++ b/src/main/java/azgracompress/compression/ImageCompressor.java @@ -15,7 +15,7 @@ public class ImageCompressor extends CompressorDecompressorBase { private final IImageCompressor imageCompressor; - public ImageCompressor(CompressionOptions options) { + public ImageCompressor(final CompressionOptions options) { super(options); imageCompressor = getImageCompressor(); } @@ -25,6 +25,10 @@ public class ImageCompressor extends CompressorDecompressorBase { imageCompressor.preloadGlobalCodebook(codebookCacheFile); } + public int getBitsPerCodebookIndex() { + return this.options.getBitsPerCodebookIndex(); + } + /** * Set InputData object for compressor. * @@ -43,7 +47,7 @@ public class ImageCompressor extends CompressorDecompressorBase { * @return Correct implementation of image compressor or null if configuration is not valid. */ private IImageCompressor getImageCompressor() { - IImageCompressor compressor; + final IImageCompressor compressor; switch (options.getQuantizationType()) { case Scalar: compressor = new SQImageCompressor(options); @@ -80,7 +84,7 @@ public class ImageCompressor extends CompressorDecompressorBase { } try { imageCompressor.trainAndSaveCodebook(); - } catch (ImageCompressionException e) { + } catch (final ImageCompressionException e) { System.err.println(e.getMessage()); e.printStackTrace(); return false; @@ -91,7 +95,7 @@ public class ImageCompressor extends CompressorDecompressorBase { public int streamCompressChunk(final OutputStream outputStream, final InputData inputData) { assert (imageCompressor != null); - try (DataOutputStream compressStream = new DataOutputStream(new BufferedOutputStream(outputStream, 8192))) { + try (final DataOutputStream compressStream = new DataOutputStream(new BufferedOutputStream(outputStream, 8192))) { final long[] chunkSizes = imageCompressor.compressStreamChunk(compressStream, inputData); for (final long chunkSize : chunkSizes) { assert (chunkSize < U16.Max); @@ -99,11 +103,11 @@ public class ImageCompressor extends CompressorDecompressorBase { } return (4 * 2) + ((int) Arrays.stream(chunkSizes).sum()) + (chunkSizes.length * 2); - } catch (ImageCompressionException ice) { + } catch (final ImageCompressionException ice) { System.err.println(ice.getMessage()); return -1; - } catch (Exception e) { + } catch (final Exception e) { System.err.println(e.getMessage()); e.printStackTrace(); return -1; @@ -118,8 +122,8 @@ public class ImageCompressor extends CompressorDecompressorBase { long[] planeDataSizes = null; - try (FileOutputStream fos = new FileOutputStream(options.getOutputFilePath(), false); - DataOutputStream compressStream = new DataOutputStream(new BufferedOutputStream(fos, 8192))) { + try (final FileOutputStream fos = new FileOutputStream(options.getOutputFilePath(), false); + final DataOutputStream compressStream = new DataOutputStream(new BufferedOutputStream(fos, 8192))) { final QCMPFileHeader header = createHeader(); header.writeHeader(compressStream); @@ -129,10 +133,10 @@ public class ImageCompressor extends CompressorDecompressorBase { if (options.isVerbose()) { reportCompressionRatio(header, compressStream.size()); } - } catch (ImageCompressionException ex) { + } catch (final ImageCompressionException ex) { System.err.println(ex.getMessage()); return false; - } catch (Exception e) { + } catch (final Exception e) { e.printStackTrace(); return false; } @@ -142,10 +146,10 @@ public class ImageCompressor extends CompressorDecompressorBase { return false; } - try (RandomAccessFile raf = new RandomAccessFile(options.getOutputFilePath(), "rw")) { + try (final RandomAccessFile raf = new RandomAccessFile(options.getOutputFilePath(), "rw")) { raf.seek(PLANE_DATA_SIZES_OFFSET); writePlaneDataSizes(raf, planeDataSizes); - } catch (IOException ex) { + } catch (final IOException ex) { ex.printStackTrace(); return false; } @@ -160,7 +164,7 @@ public class ImageCompressor extends CompressorDecompressorBase { * @param planeDataSizes Written compressed plane sizes. * @throws IOException when fails to write plane data size. */ - private void writePlaneDataSizes(RandomAccessFile outStream, final long[] planeDataSizes) throws IOException { + private void writePlaneDataSizes(final RandomAccessFile outStream, final long[] planeDataSizes) throws IOException { for (final long planeDataSize : planeDataSizes) { outStream.writeInt((int) planeDataSize); } @@ -189,7 +193,7 @@ public class ImageCompressor extends CompressorDecompressorBase { * @return Valid QCMPFile header for compressed file. */ private QCMPFileHeader createHeader() { - QCMPFileHeader header = new QCMPFileHeader(); + final QCMPFileHeader header = new QCMPFileHeader(); header.setQuantizationType(options.getQuantizationType()); diff --git a/src/main/java/azgracompress/fileformat/QCMPFileHeader.java b/src/main/java/azgracompress/fileformat/QCMPFileHeader.java index 0a0755a411ced5afb2f0ee1ac8d6784221f83084..7f8ffd75af3a6d73f2dff5bf04b1e9827ae4c26a 100644 --- a/src/main/java/azgracompress/fileformat/QCMPFileHeader.java +++ b/src/main/java/azgracompress/fileformat/QCMPFileHeader.java @@ -65,12 +65,12 @@ public class QCMPFileHeader implements Cloneable { public QCMPFileHeader copyOf() { try { return (QCMPFileHeader) this.clone(); - } catch (CloneNotSupportedException e) { + } catch (final CloneNotSupportedException e) { return null; } } - public void writeHeader(DataOutputStream outputStream) throws IOException { + public void writeHeader(final DataOutputStream outputStream) throws IOException { outputStream.writeBytes(QCMP_MAGIC_VALUE); outputStream.writeByte(quantizationType.getValue()); @@ -95,16 +95,21 @@ public class QCMPFileHeader implements Cloneable { } } - public boolean readHeader(DataInputStream inputStream) throws IOException { + public boolean readHeader(final DataInputStream inputStream) throws IOException { if (inputStream.available() < BASE_QCMP_HEADER_SIZE) { return false; } - byte[] magicBuffer = new byte[QCMP_MAGIC_VALUE.length()]; - final int readFromMagic = inputStream.read(magicBuffer, 0, QCMP_MAGIC_VALUE.length()); - if (readFromMagic != QCMP_MAGIC_VALUE.length()) { - // Invalid magic value. - return false; + final byte[] magicBuffer = new byte[QCMP_MAGIC_VALUE.length()]; + + int toRead = QCMP_MAGIC_VALUE.length(); + while (toRead > 0) { + final int read = inputStream.read(magicBuffer, QCMP_MAGIC_VALUE.length() - toRead, toRead); + if (read < 0) { + // Invalid magic value. + return false; + } + toRead -= read; } magicValue = new String(magicBuffer); @@ -142,7 +147,7 @@ public class QCMPFileHeader implements Cloneable { return quantizationType; } - public void setQuantizationType(QuantizationType quantizationType) { + public void setQuantizationType(final QuantizationType quantizationType) { this.quantizationType = quantizationType; } @@ -150,7 +155,7 @@ public class QCMPFileHeader implements Cloneable { return bitsPerCodebookIndex; } - public void setBitsPerCodebookIndex(byte bitsPerCodebookIndex) { + public void setBitsPerCodebookIndex(final byte bitsPerCodebookIndex) { this.bitsPerCodebookIndex = bitsPerCodebookIndex; } @@ -158,7 +163,7 @@ public class QCMPFileHeader implements Cloneable { return codebookPerPlane; } - public void setCodebookPerPlane(boolean codebookPerPlane) { + public void setCodebookPerPlane(final boolean codebookPerPlane) { this.codebookPerPlane = codebookPerPlane; } @@ -166,7 +171,7 @@ public class QCMPFileHeader implements Cloneable { return imageSizeX; } - public void setImageSizeX(int imageSizeX) { + public void setImageSizeX(final int imageSizeX) { this.imageSizeX = imageSizeX; } @@ -174,7 +179,7 @@ public class QCMPFileHeader implements Cloneable { return imageSizeY; } - public void setImageSizeY(int imageSizeY) { + public void setImageSizeY(final int imageSizeY) { this.imageSizeY = imageSizeY; } @@ -186,7 +191,7 @@ public class QCMPFileHeader implements Cloneable { return new V3i(imageSizeX, imageSizeY, imageSizeZ); } - public void setImageSizeZ(int imageSizeZ) { + public void setImageSizeZ(final int imageSizeZ) { this.imageSizeZ = imageSizeZ; } @@ -194,7 +199,7 @@ public class QCMPFileHeader implements Cloneable { return vectorSizeX; } - public void setVectorSizeX(int vectorSizeX) { + public void setVectorSizeX(final int vectorSizeX) { this.vectorSizeX = vectorSizeX; } @@ -202,7 +207,7 @@ public class QCMPFileHeader implements Cloneable { return vectorSizeY; } - public void setVectorSizeY(int vectorSizeY) { + public void setVectorSizeY(final int vectorSizeY) { this.vectorSizeY = vectorSizeY; } @@ -210,7 +215,7 @@ public class QCMPFileHeader implements Cloneable { return vectorSizeZ; } - public void setVectorSizeZ(int vectorSizeZ) { + public void setVectorSizeZ(final int vectorSizeZ) { this.vectorSizeZ = vectorSizeZ; } diff --git a/src/main/java/azgracompress/io/InBitStream.java b/src/main/java/azgracompress/io/InBitStream.java index 7945e4e5fc4595a141cfdd4ab868f517c96555dd..9293c4b5e28dbddfc8f6fb1a0c8d991baa4f36f5 100644 --- a/src/main/java/azgracompress/io/InBitStream.java +++ b/src/main/java/azgracompress/io/InBitStream.java @@ -5,8 +5,8 @@ import java.io.InputStream; public class InBitStream implements AutoCloseable { - private InputStream inputStream; - private byte[] buffer; + private final InputStream inputStream; + private final byte[] buffer; private int bufferPosition; private int bytesAvailable; @@ -17,7 +17,7 @@ public class InBitStream implements AutoCloseable { private boolean allowReadFromUnderlyingStream = true; - public InBitStream(InputStream inputStream, final int bitsPerValue, final int bufferSize) { + public InBitStream(final InputStream inputStream, final int bitsPerValue, final int bufferSize) { this.inputStream = inputStream; this.bitsPerValue = bitsPerValue; @@ -29,8 +29,17 @@ public class InBitStream implements AutoCloseable { bitBufferSize = 0; } + /** + * Read whole buffer from input stream. + * + * @throws IOException when unable to read from input stream. + */ public void readToBuffer() throws IOException { - bytesAvailable = inputStream.read(buffer, 0, buffer.length); + int toRead = buffer.length; + while (toRead > 0) { + toRead -= inputStream.read(buffer, buffer.length - toRead, toRead); + } + bytesAvailable = buffer.length; bufferPosition = 0; } @@ -62,7 +71,7 @@ public class InBitStream implements AutoCloseable { readByteToBitBuffer(); } --bitBufferSize; - int bit = bitBuffer & (1 << bitBufferSize); + final int bit = bitBuffer & (1 << bitBufferSize); return (bit > 0 ? 1 : 0); } @@ -79,7 +88,7 @@ public class InBitStream implements AutoCloseable { } public int[] readNValues(final int n) throws IOException { - int[] values = new int[n]; + final int[] values = new int[n]; for (int i = 0; i < n; i++) { values[i] = readValue(); } diff --git a/src/main/java/azgracompress/io/loader/RawDataLoader.java b/src/main/java/azgracompress/io/loader/RawDataLoader.java index 385f17f8d66343b53f0b9d91c09d40ed9b7275c6..7dc0e781c5c54ef09b654788c5c04f2cd889fc33 100644 --- a/src/main/java/azgracompress/io/loader/RawDataLoader.java +++ b/src/main/java/azgracompress/io/loader/RawDataLoader.java @@ -22,9 +22,9 @@ public final class RawDataLoader extends BasicLoader implements IPlaneLoader { @Override public int[] loadPlaneData(final int plane) throws IOException { - byte[] buffer; + final byte[] buffer; - try (FileInputStream fileStream = new FileInputStream(inputDataInfo.getFilePath())) { + try (final FileInputStream fileStream = new FileInputStream(inputDataInfo.getFilePath())) { final long planeSize = (long) dims.getX() * (long) dims.getY() * 2; final long expectedFileSize = planeSize * dims.getZ(); final long fileSize = fileStream.getChannel().size(); @@ -41,15 +41,21 @@ public final class RawDataLoader extends BasicLoader implements IPlaneLoader { if (fileStream.skip(planeOffset) != planeOffset) { throw new IOException("Failed to skip."); } - if (fileStream.read(buffer, 0, (int) planeSize) != planeSize) { - throw new IOException("Read wrong number of bytes."); + + int toRead = (int) planeSize; + while (toRead > 0) { + final int read = fileStream.read(buffer, (int) planeSize - toRead, toRead); + if (read < 0) { + throw new IOException("Read wrong number of bytes."); + } + toRead -= read; } } return TypeConverter.unsignedShortBytesToIntArray(buffer); } @Override - public int[] loadPlanesU16Data(int[] planes) throws IOException { + public int[] loadPlanesU16Data(final int[] planes) throws IOException { if (planes.length < 1) { return new int[0]; } else if (planes.length == 1) { @@ -65,12 +71,12 @@ public final class RawDataLoader extends BasicLoader implements IPlaneLoader { throw new IOException("Integer count is too big."); } - int[] values = new int[(int) totalValueCount]; + final int[] values = new int[(int) totalValueCount]; Arrays.sort(planes); - try (FileInputStream fileStream = new FileInputStream(inputDataInfo.getFilePath()); - DataInputStream dis = new DataInputStream(new BufferedInputStream(fileStream, 8192))) { + try (final FileInputStream fileStream = new FileInputStream(inputDataInfo.getFilePath()); + final DataInputStream dis = new DataInputStream(new BufferedInputStream(fileStream, 8192))) { int lastIndex = 0; int valIndex = 0; @@ -104,10 +110,10 @@ public final class RawDataLoader extends BasicLoader implements IPlaneLoader { throw new IOException("RawFile size is too big."); } - int[] values = new int[(int) dataSize]; + final int[] values = new int[(int) dataSize]; - try (FileInputStream fileStream = new FileInputStream(inputDataInfo.getFilePath()); - DataInputStream dis = new DataInputStream(new BufferedInputStream(fileStream, 8192))) { + try (final FileInputStream fileStream = new FileInputStream(inputDataInfo.getFilePath()); + final DataInputStream dis = new DataInputStream(new BufferedInputStream(fileStream, 8192))) { for (int i = 0; i < (int) dataSize; i++) { values[i] = dis.readUnsignedShort(); @@ -123,7 +129,7 @@ public final class RawDataLoader extends BasicLoader implements IPlaneLoader { } @Override - public int[][] loadBlocks(V2i blockDim, Range<Integer> planeRange) throws IOException { + public int[][] loadBlocks(final V2i blockDim, final Range<Integer> planeRange) throws IOException { return loadBlocksImplByLoadPlaneData(blockDim, planeRange); }