// // 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(); } }