//
// DataTools.java
//
/*
LOCI Bio-Formats package for reading and converting biological file formats.
Copyright (C) 2005-@year@ Melissa Linkert, Curtis Rueden, Chris Allan,
Eric Kjellman and Brian Loranger.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package loci.formats;
import java.io.*;
import java.text.*;
import java.util.Date;
/**
* A utility class with convenience methods for
* reading, writing and decoding words.
*
* <dl><dt><b>Source code:</b></dt>
* <dd><a href="https://skyking.microscopy.wisc.edu/trac/java/browser/trunk/loci/formats/DataTools.java">Trac</a>,
* <a href="https://skyking.microscopy.wisc.edu/svn/java/trunk/loci/formats/DataTools.java">SVN</a></dd></dl>
*
* @author Curtis Rueden ctrueden at wisc.edu
* @author Chris Allan callan at blackcat.ca
* @author Melissa Linkert linkert at wisc.edu
*/
public final class DataTools {
// -- Constants --
/** Timestamp formats. */
public static final int UNIX = 0; // January 1, 1970
public static final int COBOL = 1; // January 1, 1601
/** Milliseconds until UNIX epoch. */
public static final long UNIX_EPOCH = 0;
public static final long COBOL_EPOCH = 11644444800000L;
// -- Static fields --
/**
* Persistent byte array for calling
* {@link java.io.DataInput#readFully(byte[], int, int)} efficiently.
*/
private static ThreadLocal eightBytes = new ThreadLocal() {
protected synchronized Object initialValue() {
return new byte[8];
}
};
// -- Constructor --
private DataTools() { }
// -- Data reading --
/** Reads 1 signed byte [-128, 127]. */
public static byte readSignedByte(DataInput in) throws IOException {
byte[] b = (byte[]) eightBytes.get();
in.readFully(b, 0, 1);
return b[0];
}
/** Reads 1 unsigned byte [0, 255]. */
public static short readUnsignedByte(DataInput in) throws IOException {
short q = readSignedByte(in);
if (q < 0) q += 256;
return q;
}
/** Reads 2 signed bytes [-32768, 32767]. */
public static short read2SignedBytes(DataInput in, boolean little)
throws IOException
{
byte[] b = (byte[]) eightBytes.get();
in.readFully(b, 0, 2);
return bytesToShort(b, little);
}
/** Reads 2 unsigned bytes [0, 65535]. */
public static int read2UnsignedBytes(DataInput in, boolean little)
throws IOException
{
int q = read2SignedBytes(in, little);
if (q < 0) q += 65536;
return q;
}
/** Reads 4 signed bytes [-2147483648, 2147483647]. */
public static int read4SignedBytes(DataInput in, boolean little)
throws IOException
{
byte[] b = (byte[]) eightBytes.get();
in.readFully(b, 0, 4);
return bytesToInt(b, little);
}
/** Reads 4 unsigned bytes [0, 4294967296]. */
public static long read4UnsignedBytes(DataInput in, boolean little)
throws IOException
{
long q = read4SignedBytes(in, little);
if (q < 0) q += 4294967296L;
return q;
}
/** Reads 8 signed bytes [-9223372036854775808, 9223372036854775807]. */
public static long read8SignedBytes(DataInput in, boolean little)
throws IOException
{
byte[] b = (byte[]) eightBytes.get();
in.readFully(b, 0, 8);
return bytesToLong(b, little);
}
/** Reads 4 bytes in single precision IEEE format. */
public static float readFloat(DataInput in, boolean little)
throws IOException
{
return Float.intBitsToFloat(read4SignedBytes(in, little));
}
/** Reads 8 bytes in double precision IEEE format. */
public static double readDouble(DataInput in, boolean little)
throws IOException
{
return Double.longBitsToDouble(read8SignedBytes(in, little));
}
// -- Data writing --
/** Writes a string to the given data output destination. */
public static void writeString(DataOutput out, String s)
throws IOException
{
byte[] b = s.getBytes("UTF-8");
out.write(b);
}
/** Writes an integer to the given data output destination. */
public static void writeInt(DataOutput out, int v, boolean little)
throws IOException
{
if (little) {
out.write(v & 0xFF);
out.write((v >>> 8) & 0xFF);
out.write((v >>> 16) & 0xFF);
out.write((v >>> 24) & 0xFF);
}
else {
out.write((v >>> 24) & 0xFF);
out.write((v >>> 16) & 0xFF);
out.write((v >>> 8) & 0xFF);
out.write(v & 0xFF);
}
}
/** Writes a short to the given data output destination. */
public static void writeShort(DataOutput out, int v, boolean little)
throws IOException
{
if (little) {
out.write(v & 0xFF);
out.write((v >>> 8) & 0xFF);
}
else {
out.write((v >>> 8) & 0xFF);
out.write(v & 0xFF);
}
}
// -- Word decoding --
/**
* Translates up to the first len bytes of a byte array beyond the given
* offset to a short. If there are fewer than 2 bytes in the array,
* the MSBs are all assumed to be zero (regardless of endianness).
*/
public static short bytesToShort(byte[] bytes, int off, int len,
boolean little)
{
if (bytes.length - off < len) len = bytes.length - off;
short total = 0;
for (int i=0, ndx=off; i<len; i++, ndx++) {
total |= (bytes[ndx] < 0 ? 256 + bytes[ndx] :
(int) bytes[ndx]) << ((little ? i : len - i - 1) * 8);
}
return total;
}
/**
* Translates up to the first 2 bytes of a byte array beyond the given
* offset to a short. If there are fewer than 2 bytes in the array,
* the MSBs are all assumed to be zero (regardless of endianness).
*/
public static short bytesToShort(byte[] bytes, int off, boolean little) {
return bytesToShort(bytes, off, 2, little);
}
/**
* Translates up to the first 2 bytes of a byte array to a short.
* If there are fewer than 2 bytes in the array, the MSBs are all
* assumed to be zero (regardless of endianness).
*/
public static short bytesToShort(byte[] bytes, boolean little) {
return bytesToShort(bytes, 0, 2, little);
}
/**
* Translates up to the first len bytes of a byte array byond the given
* offset to a short. If there are fewer than 2 bytes in the array,
* the MSBs are all assumed to be zero (regardless of endianness).
*/
public static short bytesToShort(short[] bytes, int off, int len,
boolean little)
{
if (bytes.length - off < len) len = bytes.length - off;
short total = 0;
for (int i=0, ndx=off; i<len; i++, ndx++) {
total |= ((int) bytes[ndx]) << ((little ? i : len - i - 1) * 8);
}
return total;
}
/**
* Translates up to the first 2 bytes of a byte array byond the given
* offset to a short. If there are fewer than 2 bytes in the array,
* the MSBs are all assumed to be zero (regardless of endianness).
*/
public static short bytesToShort(short[] bytes, int off, boolean little) {
return bytesToShort(bytes, off, 2, little);
}
/**
* Translates up to the first 2 bytes of a byte array to a short.
* If there are fewer than 2 bytes in the array, the MSBs are all
* assumed to be zero (regardless of endianness).
*/
public static short bytesToShort(short[] bytes, boolean little) {
return bytesToShort(bytes, 0, 2, little);
}
/**
* Translates up to the first len bytes of a byte array beyond the given
* offset to an int. If there are fewer than 4 bytes in the array,
* the MSBs are all assumed to be zero (regardless of endianness).
*/
public static int bytesToInt(byte[] bytes, int off, int len,
boolean little)
{
if (bytes.length - off < len) len = bytes.length - off;
int total = 0;
for (int i=0, ndx=off; i<len; i++, ndx++) {
total |= (bytes[ndx] < 0 ? 256 + bytes[ndx] :
(int) bytes[ndx]) << ((little ? i : len - i - 1) * 8);
}
return total;
}
/**
* Translates up to the first 4 bytes of a byte array beyond the given
* offset to an int. If there are fewer than 4 bytes in the array,
* the MSBs are all assumed to be zero (regardless of endianness).
*/
public static int bytesToInt(byte[] bytes, int off, boolean little) {
return bytesToInt(bytes, off, 4, little);
}
/**
* Translates up to the first 4 bytes of a byte array to an int.
* If there are fewer than 4 bytes in the array, the MSBs are all
* assumed to be zero (regardless of endianness).
*/
public static int bytesToInt(byte[] bytes, boolean little) {
return bytesToInt(bytes, 0, 4, little);
}
/**
* Translates up to the first len bytes of a byte array beyond the given
* offset to an int. If there are fewer than 4 bytes in the array,
* the MSBs are all assumed to be zero (regardless of endianness).
*/
public static int bytesToInt(short[] bytes, int off, int len,
boolean little)
{
if (bytes.length - off < len) len = bytes.length - off;
int total = 0;
for (int i=0, ndx=off; i<len; i++, ndx++) {
total |= ((int) bytes[ndx]) << ((little ? i : len - i - 1) * 8);
}
return total;
}
/**
* Translates up to the first 4 bytes of a byte array beyond the given
* offset to an int. If there are fewer than 4 bytes in the array,
* the MSBs are all assumed to be zero (regardless of endianness).
*/
public static int bytesToInt(short[] bytes, int off, boolean little) {
return bytesToInt(bytes, off, 4, little);
}
/**
* Translates up to the first 4 bytes of a byte array to an int.
* If there are fewer than 4 bytes in the array, the MSBs are all
* assumed to be zero (regardless of endianness).
*/
public static int bytesToInt(short[] bytes, boolean little) {
return bytesToInt(bytes, 0, 4, little);
}
/**
* Translates up to the first len bytes of a byte array beyond the given
* offset to a long. If there are fewer than 8 bytes in the array,
* the MSBs are all assumed to be zero (regardless of endianness).
*/
public static long bytesToLong(byte[] bytes, int off, int len,
boolean little)
{
if (bytes.length - off < len) len = bytes.length - off;
long total = 0;
for (int i=0, ndx=off; i<len; i++, ndx++) {
total |= (bytes[ndx] < 0 ? 256L + bytes[ndx] :
(long) bytes[ndx]) << ((little ? i : len - i - 1) * 8);
}
return total;
}
/**
* Translates up to the first 8 bytes of a byte array beyond the given
* offset to a long. If there are fewer than 8 bytes in the array,
* the MSBs are all assumed to be zero (regardless of endianness).
*/
public static long bytesToLong(byte[] bytes, int off, boolean little) {
return bytesToLong(bytes, off, 8, little);
}
/**
* Translates up to the first 8 bytes of a byte array to a long.
* If there are fewer than 8 bytes in the array, the MSBs are all
* assumed to be zero (regardless of endianness).
*/
public static long bytesToLong(byte[] bytes, boolean little) {
return bytesToLong(bytes, 0, 8, little);
}
/**
* Translates up to the first len bytes of a byte array beyond the given
* offset to a long. If there are fewer than 8 bytes to be translated,
* the MSBs are all assumed to be zero (regardless of endianness).
*/
public static long bytesToLong(short[] bytes, int off, int len,
boolean little)
{
if (bytes.length - off < len) len = bytes.length - off;
long total = 0;
for (int i=0, ndx=off; i<len; i++, ndx++) {
total |= ((long) bytes[ndx]) << ((little ? i : len - i - 1) * 8);
}
return total;
}
/**
* Translates up to the first 8 bytes of a byte array beyond the given
* offset to a long. If there are fewer than 8 bytes to be translated,
* the MSBs are all assumed to be zero (regardless of endianness).
*/
public static long bytesToLong(short[] bytes, int off, boolean little) {
return bytesToLong(bytes, off, 8, little);
}
/**
* Translates up to the first 8 bytes of a byte array to a long.
* If there are fewer than 8 bytes in the array, the MSBs are all
* assumed to be zero (regardless of endianness).
*/
public static long bytesToLong(short[] bytes, boolean little) {
return bytesToLong(bytes, 0, 8, little);
}
/**
* Convert a byte array to the appropriate primitive type array.
* @param b Byte array to convert.
* @param bpp Denotes the number of bytes in the returned primitive type
* (e.g. if bpp == 2, we should return an array of type short).
* @param fp If set and bpp == 4 or bpp == 8, then return floats or doubles.
* @param little Whether byte array is in little-endian order.
*/
public static Object makeDataArray(byte[] b,
int bpp, boolean fp, boolean little)
{
if (bpp == 1) return b;
else if (bpp == 2) {
short[] s = new short[b.length / 2];
for (int i=0; i<s.length; i++) {
s[i] = bytesToShort(b, i*2, 2, little);
}
return s;
}
else if (bpp == 4 && fp) {
float[] f = new float[b.length / 4];
for (int i=0; i<f.length; i++) {
f[i] = Float.intBitsToFloat(bytesToInt(b, i*4, 4, little));
}
return f;
}
else if (bpp == 4) {
int[] i = new int[b.length / 4];
for (int j=0; j<i.length; j++) {
i[j] = bytesToInt(b, j*4, 4, little);
}
return i;
}
else if (bpp == 8 && fp) {
double[] d = new double[b.length / 8];
for (int i=0; i<d.length; i++) {
d[i] = Double.longBitsToDouble(bytesToLong(b, i*8, 8, little));
}
return d;
}
else if (bpp == 8) {
long[] l = new long[b.length / 8];
for (int i=0; i<l.length; i++) {
l[i] = bytesToLong(b, i*8, 8, little);
}
return l;
}
return null;
}
// -- Byte swapping --
public static short swap(short x) {
return (short) ((x << 8) | ((x >> 8) & 0xFF));
}
public static char swap(char x) {
return (char) ((x << 8) | ((x >> 8) & 0xFF));
}
public static int swap(int x) {
return (int) ((swap((short) x) << 16) | (swap((short) (x >> 16)) & 0xFFFF));
}
public static long swap(long x) {
return (long) (((long) swap((int) x) << 32) |
((long) swap((int) (x >> 32)) & 0xFFFFFFFFL));
}
// -- Miscellaneous --
/** Remove null bytes from a string. */
public static String stripString(String toStrip) {
char[] toRtn = new char[toStrip.length()];
int counter = 0;
for (int i=0; i<toRtn.length; i++) {
if (toStrip.charAt(i) != 0) {
toRtn[counter] = toStrip.charAt(i);
counter++;
}
}
toStrip = new String(toRtn);
toStrip = toStrip.trim();
return toStrip;
}
/** Check if two filenames have the same prefix. */
public static boolean samePrefix(String s1, String s2) {
if (s1 == null || s2 == null) return false;
int n1 = s1.indexOf(".");
int n2 = s2.indexOf(".");
if ((n1 == -1) || (n2 == -1)) return false;
int slash1 = s1.lastIndexOf(File.pathSeparator);
int slash2 = s2.lastIndexOf(File.pathSeparator);
String sub1 = s1.substring((slash1 == -1) ? 0 : slash1 + 1, n1);
String sub2 = s2.substring((slash2 == -1) ? 0 : slash2 + 1, n2);
return sub1.equals(sub2) || sub1.startsWith(sub2) || sub2.startsWith(sub1);
}
/**
* Normalize the given float array so that the minimum value maps to 0.0
* and the maximum value maps to 1.0.
*/
public static float[] normalizeFloats(float[] data) {
float[] rtn = new float[data.length];
// make a quick pass through to determine the real min and max values
float min = Float.MAX_VALUE;
float max = Float.MIN_VALUE;
for (int i=0; i<data.length; i++) {
if (data[i] < min) min = data[i];
if (data[i] > max) {
max = data[i];
}
}
// now normalize; min => 0.0, max => 1.0
for (int i=0; i<rtn.length; i++) {
rtn[i] = (data[i] - min) / (max - min);
}
return rtn;
}
// -- Date handling --
/** Converts the given timestamp into an ISO 8061 date. */
public static String convertDate(long stamp, int format) {
// see http://www.merlyn.demon.co.uk/critdate.htm for more information on
// dates than you will ever need (or want)
long ms = stamp;
switch (format) {
case COBOL:
ms -= COBOL_EPOCH;
break;
}
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
StringBuffer sb = new StringBuffer();
Date d = new Date(ms);
fmt.format(d, sb, new FieldPosition(0));
return sb.toString();
}
}