Skip to content

Commit

Permalink
store uncompressed length and check on demand
Browse files Browse the repository at this point in the history
  • Loading branch information
jmthibault79 committed Dec 4, 2018
1 parent 7f56657 commit 3bcc8f3
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 32 deletions.
44 changes: 25 additions & 19 deletions src/main/java/htsjdk/samtools/cram/structure/block/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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() {
Expand All @@ -168,14 +176,18 @@ 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);
}

/**
* The size of the uncompressed content in bytes.
*/
public int getUncompressedContentSize() {
return getUncompressedContent().length;
return uncompressedLength;
}

/**
Expand Down Expand Up @@ -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);
Expand All @@ -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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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
*/
Expand All @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down

0 comments on commit 3bcc8f3

Please sign in to comment.