diff --git a/src/main/java/htsjdk/samtools/cram/structure/block/Block.java b/src/main/java/htsjdk/samtools/cram/structure/block/Block.java index c730bbcb42..4bcf1ffb2d 100644 --- a/src/main/java/htsjdk/samtools/cram/structure/block/Block.java +++ b/src/main/java/htsjdk/samtools/cram/structure/block/Block.java @@ -57,19 +57,27 @@ public abstract class Block { */ protected final byte[] compressedContent; + /** + * The length of the content stored in this block when uncompressed + */ + private final int uncompressedLength; + /** * Abstract constructor of a generic Block, to be called by subclasses. * * @param method the block compression method. Can be RAW, if uncompressed * @param type whether this is a header or data block, and which kind * @param compressedContent the compressed form of the data to be stored in this block + * @param uncompressedLength the length of the content stored in this block when uncompressed */ protected Block(final BlockCompressionMethod method, final BlockContentType type, - final byte[] compressedContent) { + final byte[] compressedContent, + final int uncompressedLength) { this.method = method; this.contentType = type; this.compressedContent = compressedContent; + this.uncompressedLength = uncompressedLength; // causes test failures. https://github.com/samtools/htsjdk/issues/1232 // if (type == BlockContentType.EXTERNAL && getContentId() == Block.NO_CONTENT_ID) { @@ -90,7 +98,7 @@ protected Block(final BlockCompressionMethod method, * @return a new {@link FileHeaderBlock} object */ public static FileHeaderBlock uncompressedFileHeaderBlock(final byte[] rawContent) { - return new FileHeaderBlock(BlockCompressionMethod.RAW, rawContent); + return new FileHeaderBlock(BlockCompressionMethod.RAW, rawContent, rawContent.length); } /** @@ -101,7 +109,7 @@ public static FileHeaderBlock uncompressedFileHeaderBlock(final byte[] rawConten * @return a new {@link CompressionHeaderBlock} object */ public static CompressionHeaderBlock uncompressedCompressionHeaderBlock(final byte[] rawContent) { - return new CompressionHeaderBlock(BlockCompressionMethod.RAW, rawContent); + return new CompressionHeaderBlock(BlockCompressionMethod.RAW, rawContent, rawContent.length); } /** @@ -112,7 +120,7 @@ public static CompressionHeaderBlock uncompressedCompressionHeaderBlock(final by * @return a new {@link SliceHeaderBlock} object */ public static SliceHeaderBlock uncompressedSliceHeaderBlock(final byte[] rawContent) { - return new SliceHeaderBlock(BlockCompressionMethod.RAW, rawContent); + return new SliceHeaderBlock(BlockCompressionMethod.RAW, rawContent, rawContent.length); } /** @@ -123,7 +131,7 @@ public static SliceHeaderBlock uncompressedSliceHeaderBlock(final byte[] rawCont * @return a new {@link CoreDataBlock} object */ public static CoreDataBlock uncompressedCoreBlock(final byte[] rawContent) { - return new CoreDataBlock(BlockCompressionMethod.RAW, rawContent); + return new CoreDataBlock(BlockCompressionMethod.RAW, rawContent, rawContent.length); } /** @@ -141,7 +149,7 @@ public static ExternalDataBlock externalDataBlock(final int contentId, final Ext throw new CRAMException("Valid Content ID required. Given: " + contentId); } - return new ExternalDataBlock(compressor.getMethod(), compressor.compress(rawContent), contentId); + return new ExternalDataBlock(compressor.getMethod(), compressor.compress(rawContent), rawContent.length, contentId); } public final BlockCompressionMethod getMethod() { @@ -168,6 +176,10 @@ public int getContentId() { } public final byte[] getUncompressedContent() { + final byte[] uncompressedContent = ExternalCompression.uncompress(method, compressedContent); + if (uncompressedContent.length != uncompressedLength) { + throw new CRAMException(String.format("Block uncompressed length did not match expected length: %04x vs %04x", uncompressedLength, uncompressedContent.length)); + } return ExternalCompression.uncompress(method, compressedContent); } @@ -175,7 +187,7 @@ public final byte[] getUncompressedContent() { * The size of the uncompressed content in bytes. */ public int getUncompressedContentSize() { - return getUncompressedContent().length; + return uncompressedLength; } /** @@ -209,7 +221,7 @@ public static Block read(final int major, InputStream inputStream) { final BlockContentType type = BlockContentType.byId(inputStream.read()); final int contentId = ITF8.readUnsignedITF8(inputStream); final int compressedSize = ITF8.readUnsignedITF8(inputStream); - final int rawSize = ITF8.readUnsignedITF8(inputStream); + final int uncompressedSize = ITF8.readUnsignedITF8(inputStream); final byte[] compressedContent = new byte[compressedSize]; InputStreamUtils.readFully(inputStream, compressedContent, 0, compressedSize); @@ -221,23 +233,17 @@ public static Block read(final int major, InputStream inputStream) { } } - // TODO: is this check worthwhile? it may be expensive. - final byte[] uncompressedContent = ExternalCompression.uncompress(method, compressedContent); - if (uncompressedContent.length != rawSize) { - throw new CRAMException(String.format("Block uncompressed size did not match expected size: %04x vs %04x", rawSize, uncompressedContent.length)); - } - switch (type) { case FILE_HEADER: - return new FileHeaderBlock(method, compressedContent); + return new FileHeaderBlock(method, compressedContent, uncompressedSize); case COMPRESSION_HEADER: - return new CompressionHeaderBlock(method, compressedContent); + return new CompressionHeaderBlock(method, compressedContent, uncompressedSize); case MAPPED_SLICE: - return new SliceHeaderBlock(method, compressedContent); + return new SliceHeaderBlock(method, compressedContent, uncompressedSize); case EXTERNAL: - return new ExternalDataBlock(method, compressedContent, contentId); + return new ExternalDataBlock(method, compressedContent, uncompressedSize, contentId); case CORE: - return new CoreDataBlock(method, compressedContent); + return new CoreDataBlock(method, compressedContent, uncompressedSize); default: throw new CRAMException("Unknown BlockContentType " + type.name()); } diff --git a/src/main/java/htsjdk/samtools/cram/structure/block/CompressionHeaderBlock.java b/src/main/java/htsjdk/samtools/cram/structure/block/CompressionHeaderBlock.java index d3378df6e5..8e5ed976e3 100644 --- a/src/main/java/htsjdk/samtools/cram/structure/block/CompressionHeaderBlock.java +++ b/src/main/java/htsjdk/samtools/cram/structure/block/CompressionHeaderBlock.java @@ -17,9 +17,12 @@ public class CompressionHeaderBlock extends Block { * * @param method the compression method used in this block * @param compressedContent the content of this block, in compressed mode + * @param uncompressedLength the length of the content stored in this block when uncompressed */ - CompressionHeaderBlock(final BlockCompressionMethod method, final byte[] compressedContent) { - super(method, type, compressedContent); + CompressionHeaderBlock(final BlockCompressionMethod method, + final byte[] compressedContent, + final int uncompressedLength) { + super(method, type, compressedContent, uncompressedLength); } /** diff --git a/src/main/java/htsjdk/samtools/cram/structure/block/CoreDataBlock.java b/src/main/java/htsjdk/samtools/cram/structure/block/CoreDataBlock.java index 6b52b59c84..9f1e28ec20 100644 --- a/src/main/java/htsjdk/samtools/cram/structure/block/CoreDataBlock.java +++ b/src/main/java/htsjdk/samtools/cram/structure/block/CoreDataBlock.java @@ -12,8 +12,11 @@ public class CoreDataBlock extends Block { * * @param method the compression method used in this block * @param compressedContent the content of this block, in compressed mode + * @param uncompressedLength the length of the content stored in this block when uncompressed */ - CoreDataBlock(final BlockCompressionMethod method, final byte[] compressedContent) { - super(method, type, compressedContent); + CoreDataBlock(final BlockCompressionMethod method, + final byte[] compressedContent, + final int uncompressedLength) { + super(method, type, compressedContent, uncompressedLength); } } diff --git a/src/main/java/htsjdk/samtools/cram/structure/block/ExternalDataBlock.java b/src/main/java/htsjdk/samtools/cram/structure/block/ExternalDataBlock.java index a3403fe42b..c3acab7931 100644 --- a/src/main/java/htsjdk/samtools/cram/structure/block/ExternalDataBlock.java +++ b/src/main/java/htsjdk/samtools/cram/structure/block/ExternalDataBlock.java @@ -1,8 +1,5 @@ package htsjdk.samtools.cram.structure.block; -import htsjdk.samtools.cram.CRAMException; -import htsjdk.samtools.cram.compression.ExternalCompressor; - /** * A Block used by Slices to store data externally */ @@ -17,10 +14,14 @@ public class ExternalDataBlock extends Block { * * @param method the compression method used in this block * @param compressedContent the content of this block, in compressed mode + * @param uncompressedLength the length of the content stored in this block when uncompressed * @param contentId the external identifier for the block */ - ExternalDataBlock(final BlockCompressionMethod method, final byte[] compressedContent, final int contentId) { - super(method, type, compressedContent); + ExternalDataBlock(final BlockCompressionMethod method, + final byte[] compressedContent, + final int uncompressedLength, + final int contentId) { + super(method, type, compressedContent, uncompressedLength); this.contentId = contentId; } diff --git a/src/main/java/htsjdk/samtools/cram/structure/block/FileHeaderBlock.java b/src/main/java/htsjdk/samtools/cram/structure/block/FileHeaderBlock.java index 84ab3fea33..d954a125d4 100644 --- a/src/main/java/htsjdk/samtools/cram/structure/block/FileHeaderBlock.java +++ b/src/main/java/htsjdk/samtools/cram/structure/block/FileHeaderBlock.java @@ -12,8 +12,11 @@ public class FileHeaderBlock extends Block { * * @param method the compression method used in this block * @param compressedContent the content of this block, in compressed mode + * @param uncompressedLength the length of the content stored in this block when uncompressed */ - FileHeaderBlock(final BlockCompressionMethod method, final byte[] compressedContent) { - super(method, type, compressedContent); + FileHeaderBlock(final BlockCompressionMethod method, + final byte[] compressedContent, + final int uncompressedLength) { + super(method, type, compressedContent, uncompressedLength); } } diff --git a/src/main/java/htsjdk/samtools/cram/structure/block/SliceHeaderBlock.java b/src/main/java/htsjdk/samtools/cram/structure/block/SliceHeaderBlock.java index 2a3af64caa..42b99278a6 100644 --- a/src/main/java/htsjdk/samtools/cram/structure/block/SliceHeaderBlock.java +++ b/src/main/java/htsjdk/samtools/cram/structure/block/SliceHeaderBlock.java @@ -14,9 +14,12 @@ public class SliceHeaderBlock extends Block { * * @param method the compression method used in this block * @param compressedContent the content of this block, in compressed mode + * @param uncompressedLength the length of the content stored in this block when uncompressed */ - SliceHeaderBlock(final BlockCompressionMethod method, final byte[] compressedContent) { - super(method, type, compressedContent); + SliceHeaderBlock(final BlockCompressionMethod method, + final byte[] compressedContent, + final int uncompressedLength) { + super(method, type, compressedContent, uncompressedLength); } /**