From 7bd259ba2b239f3eefacd5a3062efd0dd3897db7 Mon Sep 17 00:00:00 2001 From: Joel Thibault Date: Mon, 22 Oct 2018 17:01:09 -0400 Subject: [PATCH 1/9] Remove incorrect Beta Code link --- src/main/java/htsjdk/samtools/cram/structure/EncodingID.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/htsjdk/samtools/cram/structure/EncodingID.java b/src/main/java/htsjdk/samtools/cram/structure/EncodingID.java index 3073704289..bddbc1a272 100644 --- a/src/main/java/htsjdk/samtools/cram/structure/EncodingID.java +++ b/src/main/java/htsjdk/samtools/cram/structure/EncodingID.java @@ -46,7 +46,7 @@ public enum EncodingID { */ BYTE_ARRAY_STOP, /** - * http://en.wikipedia.org/wiki/Beta_Code + * Binary coding with sufficient leading 0 bits to store each value with the same length */ BETA, /** From 8bbda8738e8c73a4acfd2a7f76e66131f291738b Mon Sep 17 00:00:00 2001 From: Joel Thibault Date: Tue, 23 Oct 2018 16:02:31 -0400 Subject: [PATCH 2/9] rename Huffman*Encoding to CanonicalHuffman*Encoding --- .../samtools/cram/build/CompressionHeaderFactory.java | 10 +++++----- .../htsjdk/samtools/cram/encoding/EncodingFactory.java | 8 ++++---- ...Encoding.java => CanonicalHuffmanByteEncoding.java} | 6 +++--- ...oding.java => CanonicalHuffmanIntegerEncoding.java} | 10 +++++----- 4 files changed, 17 insertions(+), 17 deletions(-) rename src/main/java/htsjdk/samtools/cram/encoding/huffman/codec/{HuffmanByteEncoding.java => CanonicalHuffmanByteEncoding.java} (93%) rename src/main/java/htsjdk/samtools/cram/encoding/huffman/codec/{HuffmanIntegerEncoding.java => CanonicalHuffmanIntegerEncoding.java} (89%) diff --git a/src/main/java/htsjdk/samtools/cram/build/CompressionHeaderFactory.java b/src/main/java/htsjdk/samtools/cram/build/CompressionHeaderFactory.java index c958fa41b6..c013f5a876 100644 --- a/src/main/java/htsjdk/samtools/cram/build/CompressionHeaderFactory.java +++ b/src/main/java/htsjdk/samtools/cram/build/CompressionHeaderFactory.java @@ -23,7 +23,7 @@ import htsjdk.samtools.cram.encoding.ExternalByteEncoding; import htsjdk.samtools.cram.encoding.ExternalCompressor; import htsjdk.samtools.cram.encoding.ExternalIntegerEncoding; -import htsjdk.samtools.cram.encoding.huffman.codec.HuffmanIntegerEncoding; +import htsjdk.samtools.cram.encoding.huffman.codec.CanonicalHuffmanIntegerEncoding; import htsjdk.samtools.cram.encoding.rans.RANS; import htsjdk.samtools.cram.encoding.readfeatures.ReadFeature; import htsjdk.samtools.cram.encoding.readfeatures.Substitution; @@ -422,21 +422,21 @@ private EncodingDetails buildEncodingForTag(final List re case 'c': case 'C': details.params = ByteArrayLenEncoding.toParam( - HuffmanIntegerEncoding.toParam(new int[] { 1 }, new int[] { 0 }), + CanonicalHuffmanIntegerEncoding.toParam(new int[] { 1 }, new int[] { 0 }), ExternalByteEncoding.toParam(tagID)); return details; case 'I': case 'i': case 'f': details.params = ByteArrayLenEncoding.toParam( - HuffmanIntegerEncoding.toParam(new int[] { 4 }, new int[] { 0 }), + CanonicalHuffmanIntegerEncoding.toParam(new int[] { 4 }, new int[] { 0 }), ExternalByteEncoding.toParam(tagID)); return details; case 's': case 'S': details.params = ByteArrayLenEncoding.toParam( - HuffmanIntegerEncoding.toParam(new int[] { 2 }, new int[] { 0 }), + CanonicalHuffmanIntegerEncoding.toParam(new int[] { 2 }, new int[] { 0 }), ExternalByteEncoding.toParam(tagID)); return details; case 'Z': @@ -445,7 +445,7 @@ private EncodingDetails buildEncodingForTag(final List re final boolean singleSize = stats.min == stats.max; if (singleSize) { details.params = ByteArrayLenEncoding.toParam( - HuffmanIntegerEncoding.toParam(new int[] { stats.min }, new int[] { 0 }), + CanonicalHuffmanIntegerEncoding.toParam(new int[] { stats.min }, new int[] { 0 }), ExternalByteEncoding.toParam(tagID)); return details; } diff --git a/src/main/java/htsjdk/samtools/cram/encoding/EncodingFactory.java b/src/main/java/htsjdk/samtools/cram/encoding/EncodingFactory.java index 258e148a73..9f4e53e3b2 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/EncodingFactory.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/EncodingFactory.java @@ -17,8 +17,8 @@ */ package htsjdk.samtools.cram.encoding; -import htsjdk.samtools.cram.encoding.huffman.codec.HuffmanByteEncoding; -import htsjdk.samtools.cram.encoding.huffman.codec.HuffmanIntegerEncoding; +import htsjdk.samtools.cram.encoding.huffman.codec.CanonicalHuffmanByteEncoding; +import htsjdk.samtools.cram.encoding.huffman.codec.CanonicalHuffmanIntegerEncoding; import htsjdk.samtools.cram.structure.EncodingID; /** @@ -45,7 +45,7 @@ public Encoding createEncoding(final DataSeriesType valueType, case EXTERNAL: return (Encoding) new ExternalByteEncoding(); case HUFFMAN: - return (Encoding) new HuffmanByteEncoding(); + return (Encoding) new CanonicalHuffmanByteEncoding(); case NULL: return new NullEncoding(); @@ -58,7 +58,7 @@ public Encoding createEncoding(final DataSeriesType valueType, case INT: switch (id) { case HUFFMAN: - return (Encoding) new HuffmanIntegerEncoding(); + return (Encoding) new CanonicalHuffmanIntegerEncoding(); case NULL: return new NullEncoding(); case EXTERNAL: diff --git a/src/main/java/htsjdk/samtools/cram/encoding/huffman/codec/HuffmanByteEncoding.java b/src/main/java/htsjdk/samtools/cram/encoding/huffman/codec/CanonicalHuffmanByteEncoding.java similarity index 93% rename from src/main/java/htsjdk/samtools/cram/encoding/huffman/codec/HuffmanByteEncoding.java rename to src/main/java/htsjdk/samtools/cram/encoding/huffman/codec/CanonicalHuffmanByteEncoding.java index 64a6729941..7321c6dba8 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/huffman/codec/HuffmanByteEncoding.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/huffman/codec/CanonicalHuffmanByteEncoding.java @@ -28,13 +28,13 @@ import java.nio.ByteBuffer; import java.util.Map; -public class HuffmanByteEncoding implements Encoding { +public class CanonicalHuffmanByteEncoding implements Encoding { private static final EncodingID ENCODING_ID = EncodingID.HUFFMAN; private int[] bitLengths; private byte[] values; private ByteBuffer buf = ByteBuffer.allocate(1024); - public HuffmanByteEncoding() { + public CanonicalHuffmanByteEncoding() { } @Override @@ -82,7 +82,7 @@ public BitCodec buildCodec(final Map inputMap, } public static EncodingParams toParam(final byte[] bfValues, final int[] bfBitLens) { - final HuffmanByteEncoding e = new HuffmanByteEncoding(); + final CanonicalHuffmanByteEncoding e = new CanonicalHuffmanByteEncoding(); e.values = bfValues; e.bitLengths = bfBitLens; return new EncodingParams(ENCODING_ID, e.toByteArray()); diff --git a/src/main/java/htsjdk/samtools/cram/encoding/huffman/codec/HuffmanIntegerEncoding.java b/src/main/java/htsjdk/samtools/cram/encoding/huffman/codec/CanonicalHuffmanIntegerEncoding.java similarity index 89% rename from src/main/java/htsjdk/samtools/cram/encoding/huffman/codec/HuffmanIntegerEncoding.java rename to src/main/java/htsjdk/samtools/cram/encoding/huffman/codec/CanonicalHuffmanIntegerEncoding.java index 230a6644d6..c37820d33a 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/huffman/codec/HuffmanIntegerEncoding.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/huffman/codec/CanonicalHuffmanIntegerEncoding.java @@ -29,13 +29,13 @@ import java.util.Arrays; import java.util.Map; -public class HuffmanIntegerEncoding implements Encoding { +public class CanonicalHuffmanIntegerEncoding implements Encoding { private static final EncodingID ENCODING_ID = EncodingID.HUFFMAN; private int[] bitLengths; private int[] values; private final ByteBuffer buf = ByteBuffer.allocate(1024 * 10); - public HuffmanIntegerEncoding() { + public CanonicalHuffmanIntegerEncoding() { } @Override @@ -82,7 +82,7 @@ public BitCodec buildCodec(final Map inputMap, } public static EncodingParams toParam(final int[] bfValues, final int[] bfBitLens) { - final HuffmanIntegerEncoding e = new HuffmanIntegerEncoding(); + final CanonicalHuffmanIntegerEncoding e = new CanonicalHuffmanIntegerEncoding(); e.values = bfValues; e.bitLengths = bfBitLens; return new EncodingParams(ENCODING_ID, e.toByteArray()); @@ -90,8 +90,8 @@ public static EncodingParams toParam(final int[] bfValues, final int[] bfBitLens @Override public boolean equals(final Object obj) { - if (obj instanceof HuffmanIntegerEncoding) { - final HuffmanIntegerEncoding foe = (HuffmanIntegerEncoding) obj; + if (obj instanceof CanonicalHuffmanIntegerEncoding) { + final CanonicalHuffmanIntegerEncoding foe = (CanonicalHuffmanIntegerEncoding) obj; return Arrays.equals(bitLengths, foe.bitLengths) && Arrays.equals(values, foe.values); } From d06de29fb8840beb7e3fb9570637bc4ebcf7c122 Mon Sep 17 00:00:00 2001 From: Joel Thibault Date: Fri, 19 Oct 2018 11:27:16 -0400 Subject: [PATCH 3/9] External*CodecTests ITF8 and LTF8 updates --- .../java/htsjdk/samtools/cram/io/ITF8.java | 186 +++++++++++------- .../java/htsjdk/samtools/cram/io/LTF8.java | 6 + .../encoding/ExternalByteArrayCodecTest.java | 50 +++++ .../cram/encoding/ExternalByteCodecTest.java | 47 +++++ .../encoding/ExternalIntegerCodecTest.java | 40 ++++ .../samtools/cram/io/CramIntArrayTest.java | 2 +- .../htsjdk/samtools/cram/io/CramIntTest.java | 6 +- .../htsjdk/samtools/cram/io/IOTestCases.java | 67 ++++++- .../htsjdk/samtools/cram/io/ITF8Test.java | 111 ++++++++--- .../htsjdk/samtools/cram/io/LTF8Test.java | 75 +++++-- 10 files changed, 454 insertions(+), 136 deletions(-) create mode 100644 src/test/java/htsjdk/samtools/cram/encoding/ExternalByteArrayCodecTest.java create mode 100644 src/test/java/htsjdk/samtools/cram/encoding/ExternalByteCodecTest.java create mode 100644 src/test/java/htsjdk/samtools/cram/encoding/ExternalIntegerCodecTest.java diff --git a/src/main/java/htsjdk/samtools/cram/io/ITF8.java b/src/main/java/htsjdk/samtools/cram/io/ITF8.java index edaca09369..f94a2f40bb 100644 --- a/src/main/java/htsjdk/samtools/cram/io/ITF8.java +++ b/src/main/java/htsjdk/samtools/cram/io/ITF8.java @@ -8,9 +8,37 @@ /** * Methods to read and write int values as per ITF8 specification in CRAM. + * + * ITF8 encodes ints as 1 to 5 bytes depending on the highest set bit. + * + * (using 1-based counting) + * If highest bit < 8: + * write out [bits 1-8] + * Highest bit = 8-14: + * write a byte 1,0,[bits 9-14] + * write out [bits 1-8] + * Highest bit = 15-21: + * write a byte 1,1,0,[bits 17-21] + * write out [bits 9-16] + * write out [bits 1-8] + * Highest bit = 22-28: + * write a byte 1,1,1,0,[bits 25-28] + * write out [bits 17-24] + * write out [bits 9-16] + * write out [bits 1-8] + * Highest bit > 28: + * write a byte 1,1,1,1,[bits 29-32] + * write out [bits 21-28] **** note the change in pattern here + * write out [bits 13-20] + * write out [bits 5-12] + * write out [bits 1-8] + * */ public class ITF8 { + public static final int MAX_BYTES = 5; + public static final int MAX_BITS = 8 * MAX_BYTES; + /** * Reads an unsigned (32 bit) integer from an {@link InputStream}. The sign bit should be interpreted as a value bit. * @@ -41,6 +69,48 @@ public static int readUnsignedITF8(final InputStream inputStream) throws IOExcep return ((b1 & 15) << 28) | inputStream.read() << 20 | inputStream.read() << 12 | inputStream.read() << 4 | (15 & inputStream.read()); } + /** + * Reads an unsigned (32 bit) integer from a {@link ByteBuffer}. The sign bit should be interpreted as a value bit. + * + * @param buffer the bytes to read from + * @return unsigned integer value from the buffer + */ + public static int readUnsignedITF8(final ByteBuffer buffer) { + final int b1 = 0xFF & buffer.get(); + + if ((b1 & 128) == 0) + return b1; + + if ((b1 & 64) == 0) + return ((b1 & 127) << 8) | (0xFF & buffer.get()); + + if ((b1 & 32) == 0) { + final int b2 = 0xFF & buffer.get(); + final int b3 = 0xFF & buffer.get(); + return ((b1 & 63) << 16) | b2 << 8 | b3; + } + + if ((b1 & 16) == 0) + return ((b1 & 31) << 24) | (0xFF & buffer.get()) << 16 | (0xFF & buffer.get()) << 8 | (0xFF & buffer.get()); + + return ((b1 & 15) << 28) | (0xFF & buffer.get()) << 20 | (0xFF & buffer.get()) << 12 | (0xFF & buffer.get()) << 4 + | (15 & buffer.get()); + } + + /** + * Reads an unsigned (32 bit) integer from an array of bytes. The sign bit should be interpreted as a value bit. + * + * @param data the bytes to read from + * @return the value read + */ + public static int readUnsignedITF8(final byte[] data) { + final ByteBuffer buffer = ByteBuffer.wrap(data); + final int value = readUnsignedITF8(buffer); + buffer.clear(); + + return value; + } + /** * Writes an unsigned (32 bit) integer to an {@link OutputStream} encoded as ITF8. The sign bit is interpreted as a value bit. * @@ -51,32 +121,37 @@ public static int readUnsignedITF8(final InputStream inputStream) throws IOExcep */ public static int writeUnsignedITF8(final int value, final OutputStream outputStream) throws IOException { if ((value >>> 7) == 0) { + // no control bits outputStream.write(value); return 8; } if ((value >>> 14) == 0) { - outputStream.write(((value >> 8) | 128)); + // 1 control bit + outputStream.write(((value >> 8) | 0x80)); outputStream.write((value & 0xFF)); return 16; } if ((value >>> 21) == 0) { - outputStream.write(((value >> 16) | 192)); + // 2 control bits + outputStream.write(((value >> 16) | 0xC0)); outputStream.write(((value >> 8) & 0xFF)); outputStream.write((value & 0xFF)); return 24; } if ((value >>> 28) == 0) { - outputStream.write(((value >> 24) | 224)); + // 3 control bits + outputStream.write(((value >> 24) | 0xE0)); outputStream.write(((value >> 16) & 0xFF)); outputStream.write(((value >> 8) & 0xFF)); outputStream.write((value & 0xFF)); return 32; } - outputStream.write(((value >> 28) | 240)); + // 4 control bits + outputStream.write(((value >> 28) | 0xF0)); outputStream.write(((value >> 20) & 0xFF)); outputStream.write(((value >> 12) & 0xFF)); outputStream.write(((value >> 4) & 0xFF)); @@ -84,103 +159,68 @@ public static int writeUnsignedITF8(final int value, final OutputStream outputSt return 40; } - /** - * Reads an unsigned (32 bit) integer from an array of bytes. The sign bit should be interpreted as a value bit. - * - * @param data the bytes to read from - * @return the value read - */ - public static int readUnsignedITF8(final byte[] data) { - final ByteBuffer buffer = ByteBuffer.wrap(data); - final int value = readUnsignedITF8(buffer); - buffer.clear(); - - return value; - } - - /** - * Writes an unsigned (32 bit) integer to a byte new array encoded as ITF8. The sign bit is interpreted as a value bit. - * - * @param value the value to be written out - * @return the bytes holding ITF8 representation of the value - */ - public static byte[] writeUnsignedITF8(final int value) { - final ByteBuffer buffer = ByteBuffer.allocate(10); - writeUnsignedITF8(value, buffer); - - buffer.flip(); - final byte[] array = new byte[buffer.limit()]; - buffer.get(array); - - buffer.clear(); - return array; - } - - /** - * Reads an unsigned (32 bit) integer from a {@link ByteBuffer}. The sign bit should be interpreted as a value bit. - * - * @param buffer the bytes to read from - * @return unsigned integer value from the buffer - */ - public static int readUnsignedITF8(final ByteBuffer buffer) { - final int b1 = 0xFF & buffer.get(); - - if ((b1 & 128) == 0) - return b1; - - if ((b1 & 64) == 0) - return ((b1 & 127) << 8) | (0xFF & buffer.get()); - - if ((b1 & 32) == 0) { - final int b2 = 0xFF & buffer.get(); - final int b3 = 0xFF & buffer.get(); - return ((b1 & 63) << 16) | b2 << 8 | b3; - } - - if ((b1 & 16) == 0) - return ((b1 & 31) << 24) | (0xFF & buffer.get()) << 16 | (0xFF & buffer.get()) << 8 | (0xFF & buffer.get()); - - return ((b1 & 15) << 28) | (0xFF & buffer.get()) << 20 | (0xFF & buffer.get()) << 12 | (0xFF & buffer.get()) << 4 - | (15 & buffer.get()); - } - /** * Writes an unsigned (32 bit) integer to an {@link OutputStream} encoded as ITF8. The sign bit is interpreted as a value bit. * * @param value the value to be written out * @param buffer the {@link ByteBuffer} to write to */ - public static void writeUnsignedITF8(final int value, final ByteBuffer buffer) { + public static int writeUnsignedITF8(final int value, final ByteBuffer buffer) { if ((value >>> 7) == 0) { + // no control bits buffer.put((byte) value); - return; + return 8; } if ((value >>> 14) == 0) { - buffer.put((byte) ((value >> 8) | 128)); + // 1 control bit + buffer.put((byte) ((value >> 8) | 0x80)); buffer.put((byte) (value & 0xFF)); - return; + return 16; } if ((value >>> 21) == 0) { - buffer.put((byte) ((value >> 16) | 192)); + // 2 control bits + buffer.put((byte) ((value >> 16) | 0xC0)); buffer.put((byte) ((value >> 8) & 0xFF)); buffer.put((byte) (value & 0xFF)); - return; + return 24; } if ((value >>> 28) == 0) { - buffer.put((byte) ((value >> 24) | 224)); + // 3 control bits + buffer.put((byte) ((value >> 24) | 0xE0)); buffer.put((byte) ((value >> 16) & 0xFF)); buffer.put((byte) ((value >> 8) & 0xFF)); buffer.put((byte) (value & 0xFF)); - return; + return 32; } - buffer.put((byte) ((value >> 28) | 240)); + // 4 control bits + buffer.put((byte) ((value >> 28) | 0xF0)); buffer.put((byte) ((value >> 20) & 0xFF)); buffer.put((byte) ((value >> 12) & 0xFF)); buffer.put((byte) ((value >> 4) & 0xFF)); buffer.put((byte) (value & 0xFF)); + return 40; } + + /** + * Writes an unsigned (32 bit) integer to a byte new array encoded as ITF8. The sign bit is interpreted as a value bit. + * + * @param value the value to be written out + * @return the bytes holding ITF8 representation of the value + */ + public static byte[] writeUnsignedITF8(final int value) { + final ByteBuffer buffer = ByteBuffer.allocate(10); + writeUnsignedITF8(value, buffer); + + buffer.flip(); + final byte[] array = new byte[buffer.limit()]; + buffer.get(array); + + buffer.clear(); + return array; + } + } diff --git a/src/main/java/htsjdk/samtools/cram/io/LTF8.java b/src/main/java/htsjdk/samtools/cram/io/LTF8.java index 3e98ba66c4..995c8bff7b 100644 --- a/src/main/java/htsjdk/samtools/cram/io/LTF8.java +++ b/src/main/java/htsjdk/samtools/cram/io/LTF8.java @@ -7,8 +7,14 @@ /** * Methods to read and write LTF8 as per CRAM specs. + * + * LTF8 encodes longs as 1 to 9 bytes depending on the highest set bit. See {@link ITF8} for more details. */ public class LTF8 { + + public static final int MAX_BYTES = 9; + public static final int MAX_BITS = 8 * MAX_BYTES; + /** * Reads an unsigned long value from the input stream. The sign bit should be interpreted just as other bits in the value. * diff --git a/src/test/java/htsjdk/samtools/cram/encoding/ExternalByteArrayCodecTest.java b/src/test/java/htsjdk/samtools/cram/encoding/ExternalByteArrayCodecTest.java new file mode 100644 index 0000000000..c836f66e08 --- /dev/null +++ b/src/test/java/htsjdk/samtools/cram/encoding/ExternalByteArrayCodecTest.java @@ -0,0 +1,50 @@ +package htsjdk.samtools.cram.encoding; + +import htsjdk.HtsjdkTest; +import htsjdk.samtools.cram.io.IOTestCases; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.*; + +public class ExternalByteArrayCodecTest extends HtsjdkTest { + + @Test(dataProvider = "testByteArrays", dataProviderClass = IOTestCases.class) + public void codecTest(byte[] values) throws IOException { + + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + BitCodec writeCodec = new ExternalByteArrayCodec(os, null); + + // this parameter is not used - the external block is set in the constructor + writeCodec.write(null, values); + + try (InputStream is = new ByteArrayInputStream(os.toByteArray())) { + BitCodec readCodec = new ExternalByteArrayCodec(null, is); + + // this parameter is not used - the external block is set in the constructor + byte[] actual = readCodec.read(null, values.length); + Assert.assertEquals(actual, values); + } + } + } + + @Test(expectedExceptions = RuntimeException.class) + public void readWithoutLength() throws IOException { + try (InputStream is = new ByteArrayInputStream(new byte[0])) { + BitCodec readCodec = new ExternalByteArrayCodec(null, is); + + // this parameter is not used - the external block is set in the constructor + readCodec.read(null); + } + } + + @Test(expectedExceptions = EOFException.class) + public void readTooMuch() throws IOException { + try (InputStream is = new ByteArrayInputStream(new byte[0])) { + BitCodec readCodec = new ExternalByteArrayCodec(null, is); + + // this parameter is not used - the external block is set in the constructor + readCodec.read(null, 1); + } + } +} \ No newline at end of file diff --git a/src/test/java/htsjdk/samtools/cram/encoding/ExternalByteCodecTest.java b/src/test/java/htsjdk/samtools/cram/encoding/ExternalByteCodecTest.java new file mode 100644 index 0000000000..f50ae1c375 --- /dev/null +++ b/src/test/java/htsjdk/samtools/cram/encoding/ExternalByteCodecTest.java @@ -0,0 +1,47 @@ +package htsjdk.samtools.cram.encoding; + +import htsjdk.HtsjdkTest; +import htsjdk.samtools.cram.io.IOTestCases; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +public class ExternalByteCodecTest extends HtsjdkTest { + + @Test(dataProvider = "testByteLists", dataProviderClass = IOTestCases.class) + public void codecTest(List values) throws IOException { + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + BitCodec writeCodec = new ExternalByteCodec(os, null); + + for (byte value : values) { + // this parameter is not used - the external block is set in the constructor + writeCodec.write(null, value); + } + + List actual = new ArrayList<>(values.size()); + try (InputStream is = new ByteArrayInputStream(os.toByteArray())) { + BitCodec readCodec = new ExternalByteCodec(null, is); + + for (int i = 0; i < values.size(); i++) { + // this parameter is not used - the external block is set in the constructor + actual.add(readCodec.read(null)); + } + } + + Assert.assertEquals(actual, values); + } + } + + @Test(expectedExceptions = RuntimeException.class) + public void readWithLength() throws IOException { + try (InputStream is = new ByteArrayInputStream(new byte[0])) { + BitCodec readCodec = new ExternalByteCodec(null, is); + + // this parameter is not used - the external block is set in the constructor + readCodec.read(null, 1); + } + } +} \ No newline at end of file diff --git a/src/test/java/htsjdk/samtools/cram/encoding/ExternalIntegerCodecTest.java b/src/test/java/htsjdk/samtools/cram/encoding/ExternalIntegerCodecTest.java new file mode 100644 index 0000000000..22cf18ba89 --- /dev/null +++ b/src/test/java/htsjdk/samtools/cram/encoding/ExternalIntegerCodecTest.java @@ -0,0 +1,40 @@ +package htsjdk.samtools.cram.encoding; + +import htsjdk.HtsjdkTest; +import htsjdk.samtools.cram.io.*; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class ExternalIntegerCodecTest extends HtsjdkTest { + + @Test(dataProvider = "testInt32Lists", dataProviderClass = IOTestCases.class) + public void codecTest(List values) throws IOException { + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + BitCodec writeCodec = new ExternalIntegerCodec(os, null); + + for (int value : values) { + // this parameter is not used - the external block is set in the constructor + writeCodec.write(null, value); + } + + List actual = new ArrayList<>(values.size()); + try (InputStream is = new ByteArrayInputStream(os.toByteArray())) { + BitCodec readCodec = new ExternalIntegerCodec(null, is); + + for (int i = 0; i < values.size(); i++) { + // this parameter is not used - the external block is set in the constructor + actual.add(readCodec.read(null)); + } + } + + Assert.assertEquals(actual, values); + } + } +} \ No newline at end of file diff --git a/src/test/java/htsjdk/samtools/cram/io/CramIntArrayTest.java b/src/test/java/htsjdk/samtools/cram/io/CramIntArrayTest.java index 22f5b5dc2a..1bbfcc3a33 100644 --- a/src/test/java/htsjdk/samtools/cram/io/CramIntArrayTest.java +++ b/src/test/java/htsjdk/samtools/cram/io/CramIntArrayTest.java @@ -11,7 +11,7 @@ public class CramIntArrayTest extends HtsjdkTest { - @Test(dataProvider = "testInt32Arrays", dataProviderClass = IOTestCases.class) + @Test(dataProvider = "testInt32Lists", dataProviderClass = IOTestCases.class) public void runTest(List ints) throws IOException { int[] inputArray = ints.stream().mapToInt(Integer::intValue).toArray(); diff --git a/src/test/java/htsjdk/samtools/cram/io/CramIntTest.java b/src/test/java/htsjdk/samtools/cram/io/CramIntTest.java index 7e3c2de67a..5ab4402f63 100644 --- a/src/test/java/htsjdk/samtools/cram/io/CramIntTest.java +++ b/src/test/java/htsjdk/samtools/cram/io/CramIntTest.java @@ -58,7 +58,7 @@ public void checkByteArrayLittleEndian(Integer testInt, byte[] expected) { // Combinatorial tests of 2 CramInt write methods x 3 CramInt read methods - @Test(dataProvider = "testInt32Arrays", dataProviderClass = IOTestCases.class) + @Test(dataProvider = "testInt32Lists", dataProviderClass = IOTestCases.class) public void matchStreamRead(List ints) throws IOException { byte[][] inputs = {streamWritten(ints), byteArrayWritten(ints)}; @@ -72,7 +72,7 @@ public void matchStreamRead(List ints) throws IOException { } } - @Test(dataProvider = "testInt32Arrays", dataProviderClass = IOTestCases.class) + @Test(dataProvider = "testInt32Lists", dataProviderClass = IOTestCases.class) public void matchBufferRead(List ints) throws IOException { byte[][] inputs = {streamWritten(ints), byteArrayWritten(ints)}; @@ -86,7 +86,7 @@ public void matchBufferRead(List ints) throws IOException { } } - @Test(dataProvider = "testInt32Arrays", dataProviderClass = IOTestCases.class) + @Test(dataProvider = "testInt32Lists", dataProviderClass = IOTestCases.class) public void matchByteArrayRead(List ints) throws IOException { byte[][] inputs = {streamWritten(ints), byteArrayWritten(ints)}; diff --git a/src/test/java/htsjdk/samtools/cram/io/IOTestCases.java b/src/test/java/htsjdk/samtools/cram/io/IOTestCases.java index e2a3b88b61..a63ed85da4 100644 --- a/src/test/java/htsjdk/samtools/cram/io/IOTestCases.java +++ b/src/test/java/htsjdk/samtools/cram/io/IOTestCases.java @@ -35,8 +35,63 @@ private static Object[][] asDataProvider(List list) { return params; } - static List int32Tests() { - List list = new ArrayList(); + private static List byteTests() { + List list = new ArrayList<>(); + + // basics: + list.add((byte)0); + list.add((byte)1); + list.add((byte)-1); + + // scan with bits: + for (int i = 0; i < 7; i++) { + list.add((byte)((1 << i) - 2)); + list.add((byte)((1 << i) - 1)); + list.add((byte)(1 << i)); + list.add((byte)(-(1 << i) + 2)); + list.add((byte)(-(1 << i) + 1)); + list.add((byte)-(1 << i)); + } + + // special cases: + list.add(Byte.MAX_VALUE); + list.add(Byte.MIN_VALUE); + + return list; + } + + @DataProvider(name = "testByteLists") + public static Object[][] testByteValues() { + List byteTests = IOTestCases.byteTests(); + List shuffled = new ArrayList<>(byteTests); + Collections.shuffle(shuffled); + + return new Object[][]{ + {byteTests}, + {shuffled} + }; + } + + @DataProvider(name = "testByteArrays") + public static Object[][] testByteArrayValues() { + List byteTestsList = IOTestCases.byteTests(); + List shuffledList = new ArrayList<>(byteTestsList); + Collections.shuffle(shuffledList); + + byte[] byteTests = new byte[byteTestsList.size()]; + for(int i = 0; i < byteTestsList.size(); i++) byteTests[i] = byteTestsList.get(i); + + byte[] shuffled = new byte[shuffledList.size()]; + for(int i = 0; i < shuffledList.size(); i++) shuffled[i] = shuffledList.get(i); + + return new Object[][]{ + {byteTests}, + {shuffled} + }; + } + + private static List int32Tests() { + List list = new ArrayList<>(); // basics: list.add(0); @@ -69,7 +124,7 @@ public static Object[][] testInt32() { return asDataProvider(IOTestCases.int32Tests()); } - @DataProvider(name = "testInt32Arrays") + @DataProvider(name = "testInt32Lists") public static Object[][] testValues32() { List int32Tests = IOTestCases.int32Tests(); List shuffled = new ArrayList<>(int32Tests); @@ -81,8 +136,8 @@ public static Object[][] testValues32() { }; } - static List int64Tests() { - List list = new ArrayList() ; + private static List int64Tests() { + List list = new ArrayList<>() ; // basics: list.add(0L); @@ -121,7 +176,7 @@ public static Object[][] testInt64() { return asDataProvider(IOTestCases.int64Tests()); } - @DataProvider(name = "testInt64Arrays") + @DataProvider(name = "testInt64Lists") public static Object[][] testValues64() { List int64Tests = IOTestCases.int64Tests(); List shuffled = new ArrayList<>(int64Tests); diff --git a/src/test/java/htsjdk/samtools/cram/io/ITF8Test.java b/src/test/java/htsjdk/samtools/cram/io/ITF8Test.java index 749d0451bb..e49b1a1bd0 100644 --- a/src/test/java/htsjdk/samtools/cram/io/ITF8Test.java +++ b/src/test/java/htsjdk/samtools/cram/io/ITF8Test.java @@ -1,16 +1,13 @@ package htsjdk.samtools.cram.io; import htsjdk.HtsjdkTest; -import htsjdk.samtools.util.Tuple; import org.testng.Assert; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.ArrayList; +import java.io.*; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; import java.util.List; /** @@ -18,42 +15,79 @@ */ public class ITF8Test extends HtsjdkTest { - private ExposedByteArrayOutputStream testBAOS; - private ByteArrayInputStream testBAIS; + private byte[] streamWritten(List ints) throws IOException { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + for (int value : ints) { + int len = ITF8.writeUnsignedITF8(value, baos); + Assert.assertTrue(len <= ITF8.MAX_BITS); - @BeforeClass - public void initialize() { - testBAOS = new ExposedByteArrayOutputStream(); - testBAIS = new ByteArrayInputStream(testBAOS.getBuffer()); + } + return baos.toByteArray(); + } } - @BeforeMethod - public void reset() { - testBAOS.reset(); - testBAIS.reset(); + private byte[] byteBufferWritten(List ints) { + final int arraySize = ITF8.MAX_BYTES * ints.size(); + ByteBuffer bb = ByteBuffer.wrap(new byte[arraySize]); + + for (int value : ints) { + int len = ITF8.writeUnsignedITF8(value, bb); + Assert.assertTrue(len <= ITF8.MAX_BITS); + } + + return bb.array(); + } + + // Combinatorial tests of 2 ITF8 write methods x 2 ITF8 read methods + + @Test(dataProvider = "testInt32Lists", dataProviderClass = IOTestCases.class) + public void testITF8Stream(List ints) throws IOException { + byte[][] inputs = {streamWritten(ints), byteBufferWritten(ints)}; + + for (byte[] byteArray : inputs) { + try (InputStream testBAIS = new ByteArrayInputStream(byteArray)) { + for (int value : ints) { + int fromStream = ITF8.readUnsignedITF8(testBAIS); + Assert.assertEquals(fromStream, value); + } + } + } } - @Test(dataProvider = "testInt32", dataProviderClass = IOTestCases.class) - public void testITF8(int value) throws IOException { - int len = ITF8.writeUnsignedITF8(value, testBAOS); - Assert.assertTrue(len <= (8 * 9)); + @Test(dataProvider = "testInt32Lists", dataProviderClass = IOTestCases.class) + public void testITF8Buffer(List ints) throws IOException { + byte[][] inputs = {streamWritten(ints), byteBufferWritten(ints)}; - long result = ITF8.readUnsignedITF8(testBAIS); - Assert.assertEquals(value, result); + for (byte[] byteArray : inputs) { + ByteBuffer bb = ByteBuffer.wrap(byteArray); + for (int value : ints) { + int fromStream = ITF8.readUnsignedITF8(bb); + Assert.assertEquals(fromStream, value); + } + } } @DataProvider(name = "predefined") public static Object[][] predefinedProvider() { - List> list = new ArrayList>() ; - list.add(new Tuple(4542278, new byte[]{(byte) (0xFF & 224), 69, 79, 70})) ; - list.add(new Tuple(16384, new byte[]{-64, 64, 0})) ; - list.add(new Tuple(192, new byte[]{-128, -64})) ; - list.add(new Tuple(-4757, new byte[]{-1, -1, -2, -42, 107})) ; - - Object[][] params = new Object[list.size()][] ; - for (int i=0; i 0xE0 + // write 3 bytes as-is -> 0x45, 0x4F, 0x46 + {4542278, new byte[]{(byte) (0xFF & 224), 69, 79, 70}}, + // 0x4000 has highest bit 15 + // set 2 high bits + bits 17-21 (all 0) -> 0xC0 + // write 2 bytes as-is -> 0x40, 0x00 + {16384, new byte[]{-64, 64, 0}}, + // 0xC0 has highest bit 8 + // set 1 high bit + bits 9-14 (all 0) -> 0x80 + // write 1 byte as-is -> 0xC0 + {192, new byte[]{-128, -64}}, + // 0xFFFFED6B has highest bit 32 (as all negatives do) + // set 4 high bits + bits 29-32 (all 1) -> 0xFF + // write bits 5-28 as 3 bytes -> 0xFF, 0xFE, 0xD6 + // write lowest 1 byte as-is (duplicating bits 5-8) -> 0x6B + {-4757, new byte[]{-1, -1, -2, -42, 107}} + }; } @Test(dataProvider = "predefined") @@ -64,4 +98,17 @@ public void testPredefined (int value, byte[] itf8) { int n = ITF8.readUnsignedITF8(itf8); Assert.assertEquals(value, n); } + + @Test(expectedExceptions = EOFException.class) + public void emptyStreamTest() throws IOException { + try (InputStream emptyStream = new ByteArrayInputStream(new byte[0])) { + ITF8.readUnsignedITF8(emptyStream); + } + } + + @Test(expectedExceptions = BufferUnderflowException.class) + public void emptyBufferTest() { + ByteBuffer bb = ByteBuffer.wrap(new byte[0]); + ITF8.readUnsignedITF8(bb); + } } diff --git a/src/test/java/htsjdk/samtools/cram/io/LTF8Test.java b/src/test/java/htsjdk/samtools/cram/io/LTF8Test.java index a75d1c0781..7afd161fb0 100644 --- a/src/test/java/htsjdk/samtools/cram/io/LTF8Test.java +++ b/src/test/java/htsjdk/samtools/cram/io/LTF8Test.java @@ -2,13 +2,10 @@ import htsjdk.HtsjdkTest; import org.testng.Assert; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import java.io.ByteArrayInputStream; -import java.io.IOException; +import java.io.*; import java.util.List; /** @@ -16,28 +13,64 @@ */ public class LTF8Test extends HtsjdkTest { - private ExposedByteArrayOutputStream ltf8TestBAOS; - private ByteArrayInputStream ltf8TestBAIS; - - @BeforeClass - public void initialize() { - ltf8TestBAOS = new ExposedByteArrayOutputStream(); - ltf8TestBAIS = new ByteArrayInputStream(ltf8TestBAOS.getBuffer()); + @Test(dataProvider = "testInt64Lists", dataProviderClass = IOTestCases.class) + public void testITF8Stream(List longs) throws IOException { + try (ByteArrayOutputStream ltf8TestBAOS = new ByteArrayOutputStream()) { + for (long value : longs) { + final int len = LTF8.writeUnsignedLTF8(value, ltf8TestBAOS); + Assert.assertTrue(len <= LTF8.MAX_BITS); + } + try (ByteArrayInputStream ltf8TestBAIS = new ByteArrayInputStream(ltf8TestBAOS.toByteArray())) { + for (long value : longs) { + final long result = LTF8.readUnsignedLTF8(ltf8TestBAIS); + Assert.assertEquals(value, result); + } + } + } } - @BeforeMethod - public void reset() { - ltf8TestBAOS.reset(); - ltf8TestBAIS.reset(); + @DataProvider(name = "predefined") + public static Object[][] predefinedProvider() { + return new Object[][]{ + // 0x454F46 has highest bit 23 + // set 3 high bits + bits 25-28 (all 0) -> 0xE0 + // write 3 bytes as-is -> 0x45, 0x4F, 0x46 + {4542278, new byte[]{(byte) (0xFF & 224), 69, 79, 70}}, + // 0x4000 has highest bit 15 + // set 2 high bits + bits 17-21 (all 0) -> 0xC0 + // write 2 bytes as-is -> 0x40, 0x00 + {16384, new byte[]{-64, 64, 0}}, + // 0xC0 has highest bit 8 + // set 1 high bit + bits 9-14 (all 0) -> 0x80 + // write 1 byte as-is -> 0xC0 + {192, new byte[]{-128, -64}}, + // 0xFFFF FFFF FFFF ED6B has highest bit 64 (as all negatives do) + // set 8 high bits (all 1) -> 0xFF + // write 8 bytes as-is -> 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0x6B + {-4757, new byte[]{-1, -1, -1, -1, -1, -1, -1, -19, 107}} + }; } + @Test(dataProvider = "predefined") + public void testPredefined (long value, byte[] ltf8) throws IOException { + try (ByteArrayOutputStream ltf8TestBAOS = new ByteArrayOutputStream()) { + final int len = LTF8.writeUnsignedLTF8(value, ltf8TestBAOS); + Assert.assertTrue(len <= LTF8.MAX_BITS); + + byte[] actualArray = ltf8TestBAOS.toByteArray(); + Assert.assertEquals(ltf8, actualArray); - @Test(dataProvider = "testInt64", dataProviderClass = IOTestCases.class) - public void testLTF8(long value) throws IOException { - int len = LTF8.writeUnsignedLTF8(value, ltf8TestBAOS); - Assert.assertTrue(len <= (8 * 9)); + try (ByteArrayInputStream ltf8TestBAIS = new ByteArrayInputStream(actualArray)) { + final long actual = LTF8.readUnsignedLTF8(ltf8TestBAIS); + Assert.assertEquals(actual, value); + } + } + } - long result = LTF8.readUnsignedLTF8(ltf8TestBAIS); - Assert.assertEquals(value, result); + @Test(expectedExceptions = EOFException.class) + public void emptyStreamTest() throws IOException { + try (InputStream emptyStream = new ByteArrayInputStream(new byte[0])) { + LTF8.readUnsignedLTF8(emptyStream); + } } } From 05a77d7525a4749b334f703fb63aa3c7acc0b823 Mon Sep 17 00:00:00 2001 From: Joel Thibault Date: Mon, 29 Oct 2018 14:34:52 -0400 Subject: [PATCH 4/9] make ITF8/LTF8 consts pkg-priv --- src/main/java/htsjdk/samtools/cram/io/ITF8.java | 4 ++-- src/main/java/htsjdk/samtools/cram/io/LTF8.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/htsjdk/samtools/cram/io/ITF8.java b/src/main/java/htsjdk/samtools/cram/io/ITF8.java index f94a2f40bb..eada503b53 100644 --- a/src/main/java/htsjdk/samtools/cram/io/ITF8.java +++ b/src/main/java/htsjdk/samtools/cram/io/ITF8.java @@ -36,8 +36,8 @@ */ public class ITF8 { - public static final int MAX_BYTES = 5; - public static final int MAX_BITS = 8 * MAX_BYTES; + static final int MAX_BYTES = 5; + static final int MAX_BITS = 8 * MAX_BYTES; /** * Reads an unsigned (32 bit) integer from an {@link InputStream}. The sign bit should be interpreted as a value bit. diff --git a/src/main/java/htsjdk/samtools/cram/io/LTF8.java b/src/main/java/htsjdk/samtools/cram/io/LTF8.java index 995c8bff7b..c0406aa3ce 100644 --- a/src/main/java/htsjdk/samtools/cram/io/LTF8.java +++ b/src/main/java/htsjdk/samtools/cram/io/LTF8.java @@ -12,8 +12,8 @@ */ public class LTF8 { - public static final int MAX_BYTES = 9; - public static final int MAX_BITS = 8 * MAX_BYTES; + static final int MAX_BYTES = 9; + static final int MAX_BITS = 8 * MAX_BYTES; /** * Reads an unsigned long value from the input stream. The sign bit should be interpreted just as other bits in the value. From 50d42e16abd1164302505b2ac004f55909ce6588 Mon Sep 17 00:00:00 2001 From: Joel Thibault Date: Mon, 29 Oct 2018 15:00:28 -0400 Subject: [PATCH 5/9] ITF8/LTF8: rewrap IOException as RuntimeIOException --- .../cram/encoding/ExternalIntegerCodec.java | 7 +- .../htsjdk/samtools/cram/io/CramIntArray.java | 4 +- .../java/htsjdk/samtools/cram/io/ITF8.java | 117 ++++--- .../java/htsjdk/samtools/cram/io/LTF8.java | 308 +++++++++--------- 4 files changed, 224 insertions(+), 212 deletions(-) diff --git a/src/main/java/htsjdk/samtools/cram/encoding/ExternalIntegerCodec.java b/src/main/java/htsjdk/samtools/cram/encoding/ExternalIntegerCodec.java index d26b0db6eb..aed2e14114 100644 --- a/src/main/java/htsjdk/samtools/cram/encoding/ExternalIntegerCodec.java +++ b/src/main/java/htsjdk/samtools/cram/encoding/ExternalIntegerCodec.java @@ -61,12 +61,7 @@ public long write(final BitOutputStream bitOutputStream, final Integer value) th @Override public long numberOfBits(final Integer value) { - try { - return ITF8.writeUnsignedITF8(value, nullOutputStream); - } catch (final IOException e) { - // this should never happened but still: - throw new RuntimeException(e); - } + return ITF8.writeUnsignedITF8(value, nullOutputStream); } @Override diff --git a/src/main/java/htsjdk/samtools/cram/io/CramIntArray.java b/src/main/java/htsjdk/samtools/cram/io/CramIntArray.java index 8ce5421de5..5de4472d8e 100644 --- a/src/main/java/htsjdk/samtools/cram/io/CramIntArray.java +++ b/src/main/java/htsjdk/samtools/cram/io/CramIntArray.java @@ -15,7 +15,7 @@ public class CramIntArray { * @return array of integers from the input stream * @throws IOException as per java IO contract */ - public static int[] array(final InputStream inputStream) throws IOException { + public static int[] array(final InputStream inputStream) { final int size = ITF8.readUnsignedITF8(inputStream); final int[] array = new int[size]; for (int i = 0; i < size; i++) @@ -32,7 +32,7 @@ public static int[] array(final InputStream inputStream) throws IOException { * @return the number of bits written out * @throws IOException as per java IO contract */ - public static int write(final int[] array, final OutputStream outputStream) throws IOException { + public static int write(final int[] array, final OutputStream outputStream) { int length = ITF8.writeUnsignedITF8(array.length, outputStream); for (final int intValue : array) length += ITF8.writeUnsignedITF8(intValue, outputStream); diff --git a/src/main/java/htsjdk/samtools/cram/io/ITF8.java b/src/main/java/htsjdk/samtools/cram/io/ITF8.java index eada503b53..3de4d3a9a7 100644 --- a/src/main/java/htsjdk/samtools/cram/io/ITF8.java +++ b/src/main/java/htsjdk/samtools/cram/io/ITF8.java @@ -1,5 +1,7 @@ package htsjdk.samtools.cram.io; +import htsjdk.samtools.util.RuntimeIOException; + import java.io.EOFException; import java.io.IOException; import java.io.InputStream; @@ -44,29 +46,33 @@ public class ITF8 { * * @param inputStream the stream to read from * @return the value read - * @throws IOException as per java IO contract */ - public static int readUnsignedITF8(final InputStream inputStream) throws IOException { - final int b1 = inputStream.read(); - if (b1 == -1) - throw new EOFException(); + public static int readUnsignedITF8(final InputStream inputStream) { + try { + final int b1 = inputStream.read(); + if (b1 == -1) + throw new EOFException(); - if ((b1 & 128) == 0) - return b1; + if ((b1 & 128) == 0) + return b1; - if ((b1 & 64) == 0) - return ((b1 & 127) << 8) | inputStream.read(); + if ((b1 & 64) == 0) + return ((b1 & 127) << 8) | inputStream.read(); - if ((b1 & 32) == 0) { - final int b2 = inputStream.read(); - final int b3 = inputStream.read(); - return ((b1 & 63) << 16) | b2 << 8 | b3; - } + if ((b1 & 32) == 0) { + final int b2 = inputStream.read(); + final int b3 = inputStream.read(); + return ((b1 & 63) << 16) | b2 << 8 | b3; + } - if ((b1 & 16) == 0) - return ((b1 & 31) << 24) | inputStream.read() << 16 | inputStream.read() << 8 | inputStream.read(); + if ((b1 & 16) == 0) + return ((b1 & 31) << 24) | inputStream.read() << 16 | inputStream.read() << 8 | inputStream.read(); - return ((b1 & 15) << 28) | inputStream.read() << 20 | inputStream.read() << 12 | inputStream.read() << 4 | (15 & inputStream.read()); + return ((b1 & 15) << 28) | inputStream.read() << 20 | inputStream.read() << 12 | inputStream.read() << 4 | (15 & inputStream.read()); + } + catch (IOException e) { + throw new RuntimeIOException(e); + } } /** @@ -117,46 +123,49 @@ public static int readUnsignedITF8(final byte[] data) { * @param value the value to be written out * @param outputStream the stream to write to * @return number of bits written - * @throws IOException as per java IO contract */ - public static int writeUnsignedITF8(final int value, final OutputStream outputStream) throws IOException { - if ((value >>> 7) == 0) { - // no control bits - outputStream.write(value); - return 8; - } - - if ((value >>> 14) == 0) { - // 1 control bit - outputStream.write(((value >> 8) | 0x80)); + public static int writeUnsignedITF8(final int value, final OutputStream outputStream) { + try { + if ((value >>> 7) == 0) { + // no control bits + outputStream.write(value); + return 8; + } + + if ((value >>> 14) == 0) { + // 1 control bit + outputStream.write(((value >> 8) | 0x80)); + outputStream.write((value & 0xFF)); + return 16; + } + + if ((value >>> 21) == 0) { + // 2 control bits + outputStream.write(((value >> 16) | 0xC0)); + outputStream.write(((value >> 8) & 0xFF)); + outputStream.write((value & 0xFF)); + return 24; + } + + if ((value >>> 28) == 0) { + // 3 control bits + outputStream.write(((value >> 24) | 0xE0)); + outputStream.write(((value >> 16) & 0xFF)); + outputStream.write(((value >> 8) & 0xFF)); + outputStream.write((value & 0xFF)); + return 32; + } + + // 4 control bits + outputStream.write(((value >> 28) | 0xF0)); + outputStream.write(((value >> 20) & 0xFF)); + outputStream.write(((value >> 12) & 0xFF)); + outputStream.write(((value >> 4) & 0xFF)); outputStream.write((value & 0xFF)); - return 16; - } - - if ((value >>> 21) == 0) { - // 2 control bits - outputStream.write(((value >> 16) | 0xC0)); - outputStream.write(((value >> 8) & 0xFF)); - outputStream.write((value & 0xFF)); - return 24; - } - - if ((value >>> 28) == 0) { - // 3 control bits - outputStream.write(((value >> 24) | 0xE0)); - outputStream.write(((value >> 16) & 0xFF)); - outputStream.write(((value >> 8) & 0xFF)); - outputStream.write((value & 0xFF)); - return 32; + return 40; + } catch (IOException e) { + throw new RuntimeIOException(e); } - - // 4 control bits - outputStream.write(((value >> 28) | 0xF0)); - outputStream.write(((value >> 20) & 0xFF)); - outputStream.write(((value >> 12) & 0xFF)); - outputStream.write(((value >> 4) & 0xFF)); - outputStream.write((value & 0xFF)); - return 40; } /** diff --git a/src/main/java/htsjdk/samtools/cram/io/LTF8.java b/src/main/java/htsjdk/samtools/cram/io/LTF8.java index c0406aa3ce..2ddb378c79 100644 --- a/src/main/java/htsjdk/samtools/cram/io/LTF8.java +++ b/src/main/java/htsjdk/samtools/cram/io/LTF8.java @@ -1,5 +1,7 @@ package htsjdk.samtools.cram.io; +import htsjdk.samtools.util.RuntimeIOException; + import java.io.EOFException; import java.io.IOException; import java.io.InputStream; @@ -20,65 +22,76 @@ public class LTF8 { * * @param inputStream input stream to be read from * @return value encoded in the stream as LTF8 - * @throws IOException as per java IO contract */ - public static long readUnsignedLTF8(final InputStream inputStream) throws IOException { - final int b1 = inputStream.read(); - if (b1 == -1) - throw new EOFException(); - - if ((b1 & 128) == 0) - return b1; - - if ((b1 & 64) == 0) - return ((b1 & 127) << 8) | inputStream.read(); - - if ((b1 & 32) == 0) { - final int b2 = inputStream.read(); - final int b3 = inputStream.read(); - return ((b1 & 63) << 16) | b2 << 8 | b3; - } - - if ((b1 & 16) == 0) { - long result = ((long) (b1 & 31) << 24); - result |= inputStream.read() << 16; - result |= inputStream.read() << 8; - result |= inputStream.read(); - return result; - } - - if ((b1 & 8) == 0) { - long value = ((long) (b1 & 15) << 32); - value |= ((0xFF & ((long) inputStream.read())) << 24); - value |= (inputStream.read() << 16); - value |= (inputStream.read() << 8); - value |= inputStream.read(); - return value; - } - - if ((b1 & 4) == 0) { - long result = ((long) (b1 & 7) << 40); - result |= (0xFF & ((long) inputStream.read())) << 32; - result |= (0xFF & ((long) inputStream.read())) << 24; - result |= inputStream.read() << 16; - result |= inputStream.read() << 8; - result |= inputStream.read(); - return result; - } - - if ((b1 & 2) == 0) { - long result = ((long) (b1 & 3) << 48); - result |= (0xFF & ((long) inputStream.read())) << 40; - result |= (0xFF & ((long) inputStream.read())) << 32; - result |= (0xFF & ((long) inputStream.read())) << 24; - result |= inputStream.read() << 16; - result |= inputStream.read() << 8; - result |= inputStream.read(); - return result; - } - - if ((b1 & 1) == 0) { - long result = (0xFF & ((long) inputStream.read())) << 48; + public static long readUnsignedLTF8(final InputStream inputStream) { + try { + final int b1 = inputStream.read(); + if (b1 == -1) + throw new EOFException(); + + if ((b1 & 128) == 0) + return b1; + + if ((b1 & 64) == 0) + return ((b1 & 127) << 8) | inputStream.read(); + + if ((b1 & 32) == 0) { + final int b2 = inputStream.read(); + final int b3 = inputStream.read(); + return ((b1 & 63) << 16) | b2 << 8 | b3; + } + + if ((b1 & 16) == 0) { + long result = ((long) (b1 & 31) << 24); + result |= inputStream.read() << 16; + result |= inputStream.read() << 8; + result |= inputStream.read(); + return result; + } + + if ((b1 & 8) == 0) { + long value = ((long) (b1 & 15) << 32); + value |= ((0xFF & ((long) inputStream.read())) << 24); + value |= (inputStream.read() << 16); + value |= (inputStream.read() << 8); + value |= inputStream.read(); + return value; + } + + if ((b1 & 4) == 0) { + long result = ((long) (b1 & 7) << 40); + result |= (0xFF & ((long) inputStream.read())) << 32; + result |= (0xFF & ((long) inputStream.read())) << 24; + result |= inputStream.read() << 16; + result |= inputStream.read() << 8; + result |= inputStream.read(); + return result; + } + + if ((b1 & 2) == 0) { + long result = ((long) (b1 & 3) << 48); + result |= (0xFF & ((long) inputStream.read())) << 40; + result |= (0xFF & ((long) inputStream.read())) << 32; + result |= (0xFF & ((long) inputStream.read())) << 24; + result |= inputStream.read() << 16; + result |= inputStream.read() << 8; + result |= inputStream.read(); + return result; + } + + if ((b1 & 1) == 0) { + long result = (0xFF & ((long) inputStream.read())) << 48; + result |= (0xFF & ((long) inputStream.read())) << 40; + result |= (0xFF & ((long) inputStream.read())) << 32; + result |= (0xFF & ((long) inputStream.read())) << 24; + result |= inputStream.read() << 16; + result |= inputStream.read() << 8; + result |= inputStream.read(); + return result; + } + + long result = (0xFF & ((long) inputStream.read())) << 56; + result |= (0xFF & ((long) inputStream.read())) << 48; result |= (0xFF & ((long) inputStream.read())) << 40; result |= (0xFF & ((long) inputStream.read())) << 32; result |= (0xFF & ((long) inputStream.read())) << 24; @@ -86,17 +99,9 @@ public static long readUnsignedLTF8(final InputStream inputStream) throws IOExce result |= inputStream.read() << 8; result |= inputStream.read(); return result; + } catch (IOException e) { + throw new RuntimeIOException(e); } - - long result = (0xFF & ((long) inputStream.read())) << 56; - result |= (0xFF & ((long) inputStream.read())) << 48; - result |= (0xFF & ((long) inputStream.read())) << 40; - result |= (0xFF & ((long) inputStream.read())) << 32; - result |= (0xFF & ((long) inputStream.read())) << 24; - result |= inputStream.read() << 16; - result |= inputStream.read() << 8; - result |= inputStream.read(); - return result; } /** @@ -105,95 +110,98 @@ public static long readUnsignedLTF8(final InputStream inputStream) throws IOExce * @param value the value to be written * @param outputStream the output stream to write to * @return the number of bits written - * @throws IOException as per java IO contract */ - public static int writeUnsignedLTF8(final long value, final OutputStream outputStream) throws IOException { - if ((value >>> 7) == 0) { - // no control bits - outputStream.write((int) value); - return 8; - } - - if ((value >>> 14) == 0) { - // one control bit - outputStream.write((int) ((value >> 8) | 0x80)); - outputStream.write((int) (value & 0xFF)); - return 16; - } - - if ((value >>> 21) == 0) { - // two control bits - outputStream.write((int) ((value >> 16) | 0xC0)); - outputStream.write((int) ((value >> 8) & 0xFF)); - outputStream.write((int) (value & 0xFF)); - return 24; - } - - if ((value >>> 28) == 0) { - // three control bits - outputStream.write((int) ((value >> 24) | 0xE0)); - outputStream.write((int) ((value >> 16) & 0xFF)); - outputStream.write((int) ((value >> 8) & 0xFF)); - outputStream.write((int) (value & 0xFF)); - return 32; - } - - if ((value >>> 35) == 0) { - // four control bits - outputStream.write((int) ((value >> 32) | 0xF0)); - outputStream.write((int) ((value >> 24) & 0xFF)); - outputStream.write((int) ((value >> 16) & 0xFF)); - outputStream.write((int) ((value >> 8) & 0xFF)); - outputStream.write((int) (value & 0xFF)); - return 40; - } - - if ((value >>> 42) == 0) { - // five control bits - outputStream.write((int) ((value >> 40) | 0xF8)); - outputStream.write((int) ((value >> 32) & 0xFF)); - outputStream.write((int) ((value >> 24) & 0xFF)); - outputStream.write((int) ((value >> 16) & 0xFF)); - outputStream.write((int) ((value >> 8) & 0xFF)); - outputStream.write((int) (value & 0xFF)); - return 48; - } - - if ((value >>> 49) == 0) { - // six control bits - outputStream.write((int) ((value >> 48) | 0xFC)); - outputStream.write((int) ((value >> 40) & 0xFF)); - outputStream.write((int) ((value >> 32) & 0xFF)); - outputStream.write((int) ((value >> 24) & 0xFF)); - outputStream.write((int) ((value >> 16) & 0xFF)); - outputStream.write((int) ((value >> 8) & 0xFF)); - outputStream.write((int) (value & 0xFF)); - return 56; - } - - if ((value >>> 56) == 0) { - // seven control bits - outputStream.write(0xFE); + public static int writeUnsignedLTF8(final long value, final OutputStream outputStream) { + try { + if ((value >>> 7) == 0) { + // no control bits + outputStream.write((int) value); + return 8; + } + + if ((value >>> 14) == 0) { + // one control bit + outputStream.write((int) ((value >> 8) | 0x80)); + outputStream.write((int) (value & 0xFF)); + return 16; + } + + if ((value >>> 21) == 0) { + // two control bits + outputStream.write((int) ((value >> 16) | 0xC0)); + outputStream.write((int) ((value >> 8) & 0xFF)); + outputStream.write((int) (value & 0xFF)); + return 24; + } + + if ((value >>> 28) == 0) { + // three control bits + outputStream.write((int) ((value >> 24) | 0xE0)); + outputStream.write((int) ((value >> 16) & 0xFF)); + outputStream.write((int) ((value >> 8) & 0xFF)); + outputStream.write((int) (value & 0xFF)); + return 32; + } + + if ((value >>> 35) == 0) { + // four control bits + outputStream.write((int) ((value >> 32) | 0xF0)); + outputStream.write((int) ((value >> 24) & 0xFF)); + outputStream.write((int) ((value >> 16) & 0xFF)); + outputStream.write((int) ((value >> 8) & 0xFF)); + outputStream.write((int) (value & 0xFF)); + return 40; + } + + if ((value >>> 42) == 0) { + // five control bits + outputStream.write((int) ((value >> 40) | 0xF8)); + outputStream.write((int) ((value >> 32) & 0xFF)); + outputStream.write((int) ((value >> 24) & 0xFF)); + outputStream.write((int) ((value >> 16) & 0xFF)); + outputStream.write((int) ((value >> 8) & 0xFF)); + outputStream.write((int) (value & 0xFF)); + return 48; + } + + if ((value >>> 49) == 0) { + // six control bits + outputStream.write((int) ((value >> 48) | 0xFC)); + outputStream.write((int) ((value >> 40) & 0xFF)); + outputStream.write((int) ((value >> 32) & 0xFF)); + outputStream.write((int) ((value >> 24) & 0xFF)); + outputStream.write((int) ((value >> 16) & 0xFF)); + outputStream.write((int) ((value >> 8) & 0xFF)); + outputStream.write((int) (value & 0xFF)); + return 56; + } + + if ((value >>> 56) == 0) { + // seven control bits + outputStream.write(0xFE); + outputStream.write((int) ((value >> 48) & 0xFF)); + outputStream.write((int) ((value >> 40) & 0xFF)); + outputStream.write((int) ((value >> 32) & 0xFF)); + outputStream.write((int) ((value >> 24) & 0xFF)); + outputStream.write((int) ((value >> 16) & 0xFF)); + outputStream.write((int) ((value >> 8) & 0xFF)); + outputStream.write((int) (value & 0xFF)); + return 64; + } + + // eight control bits + outputStream.write((0xFF)); + outputStream.write((int) ((value >> 56) & 0xFF)); outputStream.write((int) ((value >> 48) & 0xFF)); outputStream.write((int) ((value >> 40) & 0xFF)); outputStream.write((int) ((value >> 32) & 0xFF)); - outputStream.write((int) ((value >> 24) & 0xFF)); + outputStream.write((int) ((value >> 28) & 0xFF)); outputStream.write((int) ((value >> 16) & 0xFF)); outputStream.write((int) ((value >> 8) & 0xFF)); outputStream.write((int) (value & 0xFF)); - return 64; + return 72; + } catch (IOException e) { + throw new RuntimeIOException(e); } - - // eight control bits - outputStream.write((0xFF)); - outputStream.write((int) ((value >> 56) & 0xFF)); - outputStream.write((int) ((value >> 48) & 0xFF)); - outputStream.write((int) ((value >> 40) & 0xFF)); - outputStream.write((int) ((value >> 32) & 0xFF)); - outputStream.write((int) ((value >> 28) & 0xFF)); - outputStream.write((int) ((value >> 16) & 0xFF)); - outputStream.write((int) ((value >> 8) & 0xFF)); - outputStream.write((int) (value & 0xFF)); - return 72; } } From 5fbd2ce76ed118be1aca00d704333c40f531561a Mon Sep 17 00:00:00 2001 From: Joel Thibault Date: Mon, 29 Oct 2018 15:17:32 -0400 Subject: [PATCH 6/9] RuntimeEOFException --- src/main/java/htsjdk/samtools/cram/io/ITF8.java | 4 ++-- src/main/java/htsjdk/samtools/cram/io/LTF8.java | 4 ++-- src/test/java/htsjdk/samtools/cram/io/ITF8Test.java | 3 ++- src/test/java/htsjdk/samtools/cram/io/LTF8Test.java | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/htsjdk/samtools/cram/io/ITF8.java b/src/main/java/htsjdk/samtools/cram/io/ITF8.java index 3de4d3a9a7..03b0de9986 100644 --- a/src/main/java/htsjdk/samtools/cram/io/ITF8.java +++ b/src/main/java/htsjdk/samtools/cram/io/ITF8.java @@ -1,8 +1,8 @@ package htsjdk.samtools.cram.io; +import htsjdk.samtools.util.RuntimeEOFException; import htsjdk.samtools.util.RuntimeIOException; -import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -51,7 +51,7 @@ public static int readUnsignedITF8(final InputStream inputStream) { try { final int b1 = inputStream.read(); if (b1 == -1) - throw new EOFException(); + throw new RuntimeEOFException(); if ((b1 & 128) == 0) return b1; diff --git a/src/main/java/htsjdk/samtools/cram/io/LTF8.java b/src/main/java/htsjdk/samtools/cram/io/LTF8.java index 2ddb378c79..11872dabae 100644 --- a/src/main/java/htsjdk/samtools/cram/io/LTF8.java +++ b/src/main/java/htsjdk/samtools/cram/io/LTF8.java @@ -1,8 +1,8 @@ package htsjdk.samtools.cram.io; +import htsjdk.samtools.util.RuntimeEOFException; import htsjdk.samtools.util.RuntimeIOException; -import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -27,7 +27,7 @@ public static long readUnsignedLTF8(final InputStream inputStream) { try { final int b1 = inputStream.read(); if (b1 == -1) - throw new EOFException(); + throw new RuntimeEOFException(); if ((b1 & 128) == 0) return b1; diff --git a/src/test/java/htsjdk/samtools/cram/io/ITF8Test.java b/src/test/java/htsjdk/samtools/cram/io/ITF8Test.java index e49b1a1bd0..326d0d5d1e 100644 --- a/src/test/java/htsjdk/samtools/cram/io/ITF8Test.java +++ b/src/test/java/htsjdk/samtools/cram/io/ITF8Test.java @@ -1,6 +1,7 @@ package htsjdk.samtools.cram.io; import htsjdk.HtsjdkTest; +import htsjdk.samtools.util.RuntimeEOFException; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -99,7 +100,7 @@ public void testPredefined (int value, byte[] itf8) { Assert.assertEquals(value, n); } - @Test(expectedExceptions = EOFException.class) + @Test(expectedExceptions = RuntimeEOFException.class) public void emptyStreamTest() throws IOException { try (InputStream emptyStream = new ByteArrayInputStream(new byte[0])) { ITF8.readUnsignedITF8(emptyStream); diff --git a/src/test/java/htsjdk/samtools/cram/io/LTF8Test.java b/src/test/java/htsjdk/samtools/cram/io/LTF8Test.java index 7afd161fb0..b93db4ff5f 100644 --- a/src/test/java/htsjdk/samtools/cram/io/LTF8Test.java +++ b/src/test/java/htsjdk/samtools/cram/io/LTF8Test.java @@ -1,6 +1,7 @@ package htsjdk.samtools.cram.io; import htsjdk.HtsjdkTest; +import htsjdk.samtools.util.RuntimeEOFException; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -67,7 +68,7 @@ public void testPredefined (long value, byte[] ltf8) throws IOException { } } - @Test(expectedExceptions = EOFException.class) + @Test(expectedExceptions = RuntimeEOFException.class) public void emptyStreamTest() throws IOException { try (InputStream emptyStream = new ByteArrayInputStream(new byte[0])) { LTF8.readUnsignedLTF8(emptyStream); From a058888d5163e9bcbaedd77c8a60852937154261 Mon Sep 17 00:00:00 2001 From: Joel Thibault Date: Mon, 29 Oct 2018 15:17:48 -0400 Subject: [PATCH 7/9] final all the things --- .../cram/encoding/BetaIntegerCodecTest.java | 38 ++++++------ .../encoding/ExternalByteArrayCodecTest.java | 20 +++---- .../cram/encoding/ExternalByteCodecTest.java | 18 +++--- .../encoding/ExternalIntegerCodecTest.java | 14 ++--- .../samtools/cram/io/CramIntArrayTest.java | 10 ++-- .../htsjdk/samtools/cram/io/CramIntTest.java | 59 ++++++++++--------- .../htsjdk/samtools/cram/io/IOTestCases.java | 38 ++++++------ 7 files changed, 101 insertions(+), 96 deletions(-) diff --git a/src/test/java/htsjdk/samtools/cram/encoding/BetaIntegerCodecTest.java b/src/test/java/htsjdk/samtools/cram/encoding/BetaIntegerCodecTest.java index 742da5d81c..705e0a2d08 100644 --- a/src/test/java/htsjdk/samtools/cram/encoding/BetaIntegerCodecTest.java +++ b/src/test/java/htsjdk/samtools/cram/encoding/BetaIntegerCodecTest.java @@ -12,19 +12,19 @@ public class BetaIntegerCodecTest extends HtsjdkTest { - private void testCodec(int offset, int bitsPerValue, int[] values) throws IOException { - BitCodec codec = new BetaIntegerCodec(offset, bitsPerValue); + private void testCodec(final int offset, final int bitsPerValue, final int[] values) throws IOException { + final BitCodec codec = new BetaIntegerCodec(offset, bitsPerValue); - try (ByteArrayOutputStream os = new ByteArrayOutputStream(); - BitOutputStream bos = new DefaultBitOutputStream(os)) { + try (final ByteArrayOutputStream os = new ByteArrayOutputStream(); + final BitOutputStream bos = new DefaultBitOutputStream(os)) { - for (int value : values) { + for (final int value : values) { codec.write(bos, value); } - int[] actual = new int[values.length]; - try (InputStream is = new ByteArrayInputStream(os.toByteArray()); - DefaultBitInputStream dbis = new DefaultBitInputStream(is)) { + final int[] actual = new int[values.length]; + try (final InputStream is = new ByteArrayInputStream(os.toByteArray()); + final DefaultBitInputStream dbis = new DefaultBitInputStream(is)) { for (int i = 0; i < values.length; i++) { actual[i] = codec.read(dbis); @@ -46,7 +46,7 @@ public Object[][] basicTestData() { } @Test(dataProvider = "basicTest") - public void basicTest(int bitsPerValue, int offset, int[] values) throws IOException { + public void basicTest(final int bitsPerValue, final int offset, final int[] values) throws IOException { testCodec(offset, bitsPerValue, values); } @@ -61,7 +61,7 @@ public Object[][] basicTestNoOffsetData() { } @Test(dataProvider = "basicTestNoOffset") - public void basicTestNoOffset(int bitsPerValue, int[] values) throws IOException { + public void basicTestNoOffset(final int bitsPerValue, final int[] values) throws IOException { testCodec(0, bitsPerValue, values); } @@ -77,7 +77,7 @@ public Object[][] bitsPerValueData() { } @Test(dataProvider = "bitsPerValue", expectedExceptions = IllegalArgumentException.class) - public void bitsPerValue(int bitsPerValue) { + public void bitsPerValue(final int bitsPerValue) { new BetaIntegerCodec(0, bitsPerValue); } @@ -104,11 +104,11 @@ public Object[][] overflowData() { } @Test(dataProvider = "overflow", expectedExceptions = IllegalArgumentException.class) - public void overflow(int bitsPerValue, int offset, int value) throws IOException { - BitCodec codec = new BetaIntegerCodec(offset, bitsPerValue); + public void overflow(final int bitsPerValue, final int offset, final int value) throws IOException { + final BitCodec codec = new BetaIntegerCodec(offset, bitsPerValue); - try (ByteArrayOutputStream os = new ByteArrayOutputStream(); - BitOutputStream bos = new DefaultBitOutputStream(os)) { + try (final ByteArrayOutputStream os = new ByteArrayOutputStream(); + final BitOutputStream bos = new DefaultBitOutputStream(os)) { codec.write(bos, value); } } @@ -129,11 +129,11 @@ public Object[][] negativeTestData() { } @Test(dataProvider = "negativeTest", expectedExceptions = IllegalArgumentException.class) - public void negativeTest(int bitsPerValue, int offset, int value) throws IOException { - BitCodec codec = new BetaIntegerCodec(offset, bitsPerValue); + public void negativeTest(final int bitsPerValue, final int offset, final int value) throws IOException { + final BitCodec codec = new BetaIntegerCodec(offset, bitsPerValue); - try (ByteArrayOutputStream os = new ByteArrayOutputStream(); - BitOutputStream bos = new DefaultBitOutputStream(os)) { + try (final ByteArrayOutputStream os = new ByteArrayOutputStream(); + final BitOutputStream bos = new DefaultBitOutputStream(os)) { codec.write(bos, value); } } diff --git a/src/test/java/htsjdk/samtools/cram/encoding/ExternalByteArrayCodecTest.java b/src/test/java/htsjdk/samtools/cram/encoding/ExternalByteArrayCodecTest.java index c836f66e08..e35f5b51bb 100644 --- a/src/test/java/htsjdk/samtools/cram/encoding/ExternalByteArrayCodecTest.java +++ b/src/test/java/htsjdk/samtools/cram/encoding/ExternalByteArrayCodecTest.java @@ -10,19 +10,19 @@ public class ExternalByteArrayCodecTest extends HtsjdkTest { @Test(dataProvider = "testByteArrays", dataProviderClass = IOTestCases.class) - public void codecTest(byte[] values) throws IOException { + public void codecTest(final byte[] values) throws IOException { - try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { - BitCodec writeCodec = new ExternalByteArrayCodec(os, null); + try (final ByteArrayOutputStream os = new ByteArrayOutputStream()) { + final BitCodec writeCodec = new ExternalByteArrayCodec(os, null); // this parameter is not used - the external block is set in the constructor writeCodec.write(null, values); - try (InputStream is = new ByteArrayInputStream(os.toByteArray())) { - BitCodec readCodec = new ExternalByteArrayCodec(null, is); + try (final InputStream is = new ByteArrayInputStream(os.toByteArray())) { + final BitCodec readCodec = new ExternalByteArrayCodec(null, is); // this parameter is not used - the external block is set in the constructor - byte[] actual = readCodec.read(null, values.length); + final byte[] actual = readCodec.read(null, values.length); Assert.assertEquals(actual, values); } } @@ -30,8 +30,8 @@ public void codecTest(byte[] values) throws IOException { @Test(expectedExceptions = RuntimeException.class) public void readWithoutLength() throws IOException { - try (InputStream is = new ByteArrayInputStream(new byte[0])) { - BitCodec readCodec = new ExternalByteArrayCodec(null, is); + try (final InputStream is = new ByteArrayInputStream(new byte[0])) { + final BitCodec readCodec = new ExternalByteArrayCodec(null, is); // this parameter is not used - the external block is set in the constructor readCodec.read(null); @@ -40,8 +40,8 @@ public void readWithoutLength() throws IOException { @Test(expectedExceptions = EOFException.class) public void readTooMuch() throws IOException { - try (InputStream is = new ByteArrayInputStream(new byte[0])) { - BitCodec readCodec = new ExternalByteArrayCodec(null, is); + try (final InputStream is = new ByteArrayInputStream(new byte[0])) { + final BitCodec readCodec = new ExternalByteArrayCodec(null, is); // this parameter is not used - the external block is set in the constructor readCodec.read(null, 1); diff --git a/src/test/java/htsjdk/samtools/cram/encoding/ExternalByteCodecTest.java b/src/test/java/htsjdk/samtools/cram/encoding/ExternalByteCodecTest.java index f50ae1c375..8e1f892925 100644 --- a/src/test/java/htsjdk/samtools/cram/encoding/ExternalByteCodecTest.java +++ b/src/test/java/htsjdk/samtools/cram/encoding/ExternalByteCodecTest.java @@ -12,18 +12,18 @@ public class ExternalByteCodecTest extends HtsjdkTest { @Test(dataProvider = "testByteLists", dataProviderClass = IOTestCases.class) - public void codecTest(List values) throws IOException { - try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { - BitCodec writeCodec = new ExternalByteCodec(os, null); + public void codecTest(final List values) throws IOException { + try (final ByteArrayOutputStream os = new ByteArrayOutputStream()) { + final BitCodec writeCodec = new ExternalByteCodec(os, null); - for (byte value : values) { + for (final byte value : values) { // this parameter is not used - the external block is set in the constructor writeCodec.write(null, value); } - List actual = new ArrayList<>(values.size()); - try (InputStream is = new ByteArrayInputStream(os.toByteArray())) { - BitCodec readCodec = new ExternalByteCodec(null, is); + final List actual = new ArrayList<>(values.size()); + try (final InputStream is = new ByteArrayInputStream(os.toByteArray())) { + final BitCodec readCodec = new ExternalByteCodec(null, is); for (int i = 0; i < values.size(); i++) { // this parameter is not used - the external block is set in the constructor @@ -37,8 +37,8 @@ public void codecTest(List values) throws IOException { @Test(expectedExceptions = RuntimeException.class) public void readWithLength() throws IOException { - try (InputStream is = new ByteArrayInputStream(new byte[0])) { - BitCodec readCodec = new ExternalByteCodec(null, is); + try (final InputStream is = new ByteArrayInputStream(new byte[0])) { + final BitCodec readCodec = new ExternalByteCodec(null, is); // this parameter is not used - the external block is set in the constructor readCodec.read(null, 1); diff --git a/src/test/java/htsjdk/samtools/cram/encoding/ExternalIntegerCodecTest.java b/src/test/java/htsjdk/samtools/cram/encoding/ExternalIntegerCodecTest.java index 22cf18ba89..fc2087f1cf 100644 --- a/src/test/java/htsjdk/samtools/cram/encoding/ExternalIntegerCodecTest.java +++ b/src/test/java/htsjdk/samtools/cram/encoding/ExternalIntegerCodecTest.java @@ -15,18 +15,18 @@ public class ExternalIntegerCodecTest extends HtsjdkTest { @Test(dataProvider = "testInt32Lists", dataProviderClass = IOTestCases.class) - public void codecTest(List values) throws IOException { - try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { - BitCodec writeCodec = new ExternalIntegerCodec(os, null); + public void codecTest(final List values) throws IOException { + try (final ByteArrayOutputStream os = new ByteArrayOutputStream()) { + final BitCodec writeCodec = new ExternalIntegerCodec(os, null); - for (int value : values) { + for (final int value : values) { // this parameter is not used - the external block is set in the constructor writeCodec.write(null, value); } - List actual = new ArrayList<>(values.size()); - try (InputStream is = new ByteArrayInputStream(os.toByteArray())) { - BitCodec readCodec = new ExternalIntegerCodec(null, is); + final List actual = new ArrayList<>(values.size()); + try (final InputStream is = new ByteArrayInputStream(os.toByteArray())) { + final BitCodec readCodec = new ExternalIntegerCodec(null, is); for (int i = 0; i < values.size(); i++) { // this parameter is not used - the external block is set in the constructor diff --git a/src/test/java/htsjdk/samtools/cram/io/CramIntArrayTest.java b/src/test/java/htsjdk/samtools/cram/io/CramIntArrayTest.java index 1bbfcc3a33..749e4a070b 100644 --- a/src/test/java/htsjdk/samtools/cram/io/CramIntArrayTest.java +++ b/src/test/java/htsjdk/samtools/cram/io/CramIntArrayTest.java @@ -12,14 +12,14 @@ public class CramIntArrayTest extends HtsjdkTest { @Test(dataProvider = "testInt32Lists", dataProviderClass = IOTestCases.class) - public void runTest(List ints) throws IOException { + public void runTest(final List ints) throws IOException { - int[] inputArray = ints.stream().mapToInt(Integer::intValue).toArray(); - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + final int[] inputArray = ints.stream().mapToInt(Integer::intValue).toArray(); + try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) { CramIntArray.write(inputArray, baos); - try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray())) { - int[] outputArray = CramIntArray.array(bais); + try (final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray())) { + final int[] outputArray = CramIntArray.array(bais); Assert.assertEquals(inputArray, outputArray, "Arrays did not match"); } } diff --git a/src/test/java/htsjdk/samtools/cram/io/CramIntTest.java b/src/test/java/htsjdk/samtools/cram/io/CramIntTest.java index 5ab4402f63..751a1629b5 100644 --- a/src/test/java/htsjdk/samtools/cram/io/CramIntTest.java +++ b/src/test/java/htsjdk/samtools/cram/io/CramIntTest.java @@ -12,19 +12,19 @@ import java.util.List; public class CramIntTest extends HtsjdkTest { - private byte[] streamWritten(List ints) throws IOException { - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - for (int value : ints) { + private byte[] streamWritten(final List ints) throws IOException { + try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + for (final int value : ints) { CramInt.writeInt32(value, baos); } return baos.toByteArray(); } } - private byte[] byteArrayWritten(List ints) { + private byte[] byteArrayWritten(final List ints) { final int bufSize = 4; final int arraySize = bufSize * ints.size(); - byte[] array = new byte[arraySize]; + final byte[] array = new byte[arraySize]; int offset = 0; byte[] arrayBuffer; @@ -39,33 +39,33 @@ private byte[] byteArrayWritten(List ints) { } @Test(dataProvider = "littleEndianTests32", dataProviderClass = IOTestCases.class) - public void checkStreamLittleEndian(Integer testInt, byte[] expected) throws IOException { - List ints = new ArrayList<>(); + public void checkStreamLittleEndian(final Integer testInt, final byte[] expected) throws IOException { + final List ints = new ArrayList<>(); ints.add(testInt); - byte[] actual = streamWritten(ints); + final byte[] actual = streamWritten(ints); Assert.assertEquals(actual, expected); } @Test(dataProvider = "littleEndianTests32", dataProviderClass = IOTestCases.class) - public void checkByteArrayLittleEndian(Integer testInt, byte[] expected) { - List ints = new ArrayList<>(); + public void checkByteArrayLittleEndian(final Integer testInt, final byte[] expected) { + final List ints = new ArrayList<>(); ints.add(testInt); - byte[] actual = byteArrayWritten(ints); + final byte[] actual = byteArrayWritten(ints); Assert.assertEquals(actual, expected); } // Combinatorial tests of 2 CramInt write methods x 3 CramInt read methods @Test(dataProvider = "testInt32Lists", dataProviderClass = IOTestCases.class) - public void matchStreamRead(List ints) throws IOException { - byte[][] inputs = {streamWritten(ints), byteArrayWritten(ints)}; + public void matchStreamRead(final List ints) throws IOException { + final byte[][] inputs = {streamWritten(ints), byteArrayWritten(ints)}; - for (byte[] byteArray : inputs) { - try (ByteArrayInputStream bais = new ByteArrayInputStream(byteArray)) { - for (int value : ints) { - int fromStream = CramInt.readInt32(bais); + for (final byte[] byteArray : inputs) { + try (final ByteArrayInputStream bais = new ByteArrayInputStream(byteArray)) { + for (final int value : ints) { + final int fromStream = CramInt.readInt32(bais); Assert.assertEquals(fromStream, value, "Value did not match"); } } @@ -73,31 +73,32 @@ public void matchStreamRead(List ints) throws IOException { } @Test(dataProvider = "testInt32Lists", dataProviderClass = IOTestCases.class) - public void matchBufferRead(List ints) throws IOException { - byte[][] inputs = {streamWritten(ints), byteArrayWritten(ints)}; + public void matchBufferRead(final List ints) throws IOException { + final byte[][] inputs = {streamWritten(ints), byteArrayWritten(ints)}; - for (byte[] byteArray : inputs) { - ByteBuffer bb = ByteBuffer.wrap(byteArray); + for (final byte[] byteArray : inputs) { + final ByteBuffer bb = ByteBuffer.wrap(byteArray); - for (int value : ints) { - int fromBuffer = CramInt.readInt32(bb); + for (final int value : ints) { + final int fromBuffer = CramInt.readInt32(bb); Assert.assertEquals(fromBuffer, value, "Value did not match"); } } } @Test(dataProvider = "testInt32Lists", dataProviderClass = IOTestCases.class) - public void matchByteArrayRead(List ints) throws IOException { - byte[][] inputs = {streamWritten(ints), byteArrayWritten(ints)}; + public void matchByteArrayRead(final List ints) throws IOException { + final byte[][] inputs = {streamWritten(ints), byteArrayWritten(ints)}; - for (byte[] inputArray : inputs) { + for (final byte[] inputArray : inputs) { final int bufSize = 4; - byte[] outBuf = new byte[bufSize]; + final byte[] outBuf = new byte[bufSize]; + int offset = 0; - for (int value : ints) { + for (final int value : ints) { System.arraycopy(inputArray, offset, outBuf, 0, bufSize); - int fromBuffer = CramInt.readInt32(outBuf); + final int fromBuffer = CramInt.readInt32(outBuf); Assert.assertEquals(fromBuffer, value, "Value did not match"); offset += bufSize; } diff --git a/src/test/java/htsjdk/samtools/cram/io/IOTestCases.java b/src/test/java/htsjdk/samtools/cram/io/IOTestCases.java index a63ed85da4..4efb8b13f7 100644 --- a/src/test/java/htsjdk/samtools/cram/io/IOTestCases.java +++ b/src/test/java/htsjdk/samtools/cram/io/IOTestCases.java @@ -28,15 +28,15 @@ public static Object[][] littleEndianTests32() { }; } - private static Object[][] asDataProvider(List list) { - Object[][] params = new Object[list.size()][]; + private static Object[][] asDataProvider(final List list) { + final Object[][] params = new Object[list.size()][]; for (int i = 0; i < params.length; i++) params[i] = new Object[]{list.get(i)}; return params; } private static List byteTests() { - List list = new ArrayList<>(); + final List list = new ArrayList<>(); // basics: list.add((byte)0); @@ -62,8 +62,8 @@ private static List byteTests() { @DataProvider(name = "testByteLists") public static Object[][] testByteValues() { - List byteTests = IOTestCases.byteTests(); - List shuffled = new ArrayList<>(byteTests); + final List byteTests = IOTestCases.byteTests(); + final List shuffled = new ArrayList<>(byteTests); Collections.shuffle(shuffled); return new Object[][]{ @@ -74,15 +74,19 @@ public static Object[][] testByteValues() { @DataProvider(name = "testByteArrays") public static Object[][] testByteArrayValues() { - List byteTestsList = IOTestCases.byteTests(); - List shuffledList = new ArrayList<>(byteTestsList); + final List byteTestsList = IOTestCases.byteTests(); + final List shuffledList = new ArrayList<>(byteTestsList); Collections.shuffle(shuffledList); - byte[] byteTests = new byte[byteTestsList.size()]; - for(int i = 0; i < byteTestsList.size(); i++) byteTests[i] = byteTestsList.get(i); + final byte[] byteTests = new byte[byteTestsList.size()]; + for(int i = 0; i < byteTestsList.size(); i++) { + byteTests[i] = byteTestsList.get(i); + } - byte[] shuffled = new byte[shuffledList.size()]; - for(int i = 0; i < shuffledList.size(); i++) shuffled[i] = shuffledList.get(i); + final byte[] shuffled = new byte[shuffledList.size()]; + for(int i = 0; i < shuffledList.size(); i++) { + shuffled[i] = shuffledList.get(i); + } return new Object[][]{ {byteTests}, @@ -91,7 +95,7 @@ public static Object[][] testByteArrayValues() { } private static List int32Tests() { - List list = new ArrayList<>(); + final List list = new ArrayList<>(); // basics: list.add(0); @@ -126,8 +130,8 @@ public static Object[][] testInt32() { @DataProvider(name = "testInt32Lists") public static Object[][] testValues32() { - List int32Tests = IOTestCases.int32Tests(); - List shuffled = new ArrayList<>(int32Tests); + final List int32Tests = IOTestCases.int32Tests(); + final List shuffled = new ArrayList<>(int32Tests); Collections.shuffle(shuffled); return new Object[][]{ @@ -137,7 +141,7 @@ public static Object[][] testValues32() { } private static List int64Tests() { - List list = new ArrayList<>() ; + final List list = new ArrayList<>() ; // basics: list.add(0L); @@ -178,8 +182,8 @@ public static Object[][] testInt64() { @DataProvider(name = "testInt64Lists") public static Object[][] testValues64() { - List int64Tests = IOTestCases.int64Tests(); - List shuffled = new ArrayList<>(int64Tests); + final List int64Tests = IOTestCases.int64Tests(); + final List shuffled = new ArrayList<>(int64Tests); Collections.shuffle(shuffled); return new Object[][]{ From 4cafed7d8781a343db490dccb1f85bd90e555947 Mon Sep 17 00:00:00 2001 From: Joel Thibault Date: Wed, 31 Oct 2018 11:08:03 -0400 Subject: [PATCH 8/9] rm throws IOException --- src/main/java/htsjdk/samtools/cram/io/CramIntArray.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/htsjdk/samtools/cram/io/CramIntArray.java b/src/main/java/htsjdk/samtools/cram/io/CramIntArray.java index 5de4472d8e..65580f2222 100644 --- a/src/main/java/htsjdk/samtools/cram/io/CramIntArray.java +++ b/src/main/java/htsjdk/samtools/cram/io/CramIntArray.java @@ -13,7 +13,6 @@ public class CramIntArray { * * @param inputStream the inputs stream to read from * @return array of integers from the input stream - * @throws IOException as per java IO contract */ public static int[] array(final InputStream inputStream) { final int size = ITF8.readUnsignedITF8(inputStream); @@ -30,7 +29,6 @@ public static int[] array(final InputStream inputStream) { * @param array the array to be written * @param outputStream the output stream to write to * @return the number of bits written out - * @throws IOException as per java IO contract */ public static int write(final int[] array, final OutputStream outputStream) { int length = ITF8.writeUnsignedITF8(array.length, outputStream); From 2a1ecbf03caa1843c78e1210b3228947e4e9edc5 Mon Sep 17 00:00:00 2001 From: Joel Thibault Date: Wed, 31 Oct 2018 11:10:28 -0400 Subject: [PATCH 9/9] MAX_BYTES --- src/main/java/htsjdk/samtools/cram/io/ITF8.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/htsjdk/samtools/cram/io/ITF8.java b/src/main/java/htsjdk/samtools/cram/io/ITF8.java index 03b0de9986..28fd6eb64a 100644 --- a/src/main/java/htsjdk/samtools/cram/io/ITF8.java +++ b/src/main/java/htsjdk/samtools/cram/io/ITF8.java @@ -221,7 +221,7 @@ public static int writeUnsignedITF8(final int value, final ByteBuffer buffer) { * @return the bytes holding ITF8 representation of the value */ public static byte[] writeUnsignedITF8(final int value) { - final ByteBuffer buffer = ByteBuffer.allocate(10); + final ByteBuffer buffer = ByteBuffer.allocate(MAX_BYTES); writeUnsignedITF8(value, buffer); buffer.flip();