htsjdk.samtools.cram.io.ITF8 Maven / Gradle / Ivy
package htsjdk.samtools.cram.io;
import htsjdk.samtools.util.RuntimeEOFException;
import htsjdk.samtools.util.RuntimeIOException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* 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;
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.
*
* @param inputStream the stream to read from
* @return the value read
*/
public static int readUnsignedITF8(final InputStream inputStream) {
try {
final int b1 = inputStream.read();
if (b1 == -1)
throw new RuntimeEOFException();
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)
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());
}
catch (IOException e) {
throw new RuntimeIOException(e);
}
}
/**
* 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.
*
* @param value the value to be written out
* @param outputStream the stream to write to
* @return number of bits written
*/
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 40;
} catch (IOException e) {
throw new RuntimeIOException(e);
}
}
/**
* 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 int writeUnsignedITF8(final int value, final ByteBuffer buffer) {
if ((value >>> 7) == 0) {
// no control bits
buffer.put((byte) value);
return 8;
}
if ((value >>> 14) == 0) {
// 1 control bit
buffer.put((byte) ((value >> 8) | 0x80));
buffer.put((byte) (value & 0xFF));
return 16;
}
if ((value >>> 21) == 0) {
// 2 control bits
buffer.put((byte) ((value >> 16) | 0xC0));
buffer.put((byte) ((value >> 8) & 0xFF));
buffer.put((byte) (value & 0xFF));
return 24;
}
if ((value >>> 28) == 0) {
// 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 32;
}
// 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(MAX_BYTES);
writeUnsignedITF8(value, buffer);
buffer.flip();
final byte[] array = new byte[buffer.limit()];
buffer.get(array);
buffer.clear();
return array;
}
}