/* * #%L * Common package for I/O and related utilities * %% * Copyright (C) 2005 - 2015 Open Microscopy Environment: * - Board of Regents of the University of Wisconsin-Madison * - Glencoe Software, Inc. * - University of Dundee * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * #L% */ package loci.common; import java.io.File; import java.io.IOException; import java.text.DecimalFormatSymbols; /** * A utility class with convenience methods for * reading, writing and decoding words. * * @author Curtis Rueden ctrueden at wisc.edu * @author Chris Allan callan at blackcat.ca * @author Melissa Linkert melissa at glencoesoftware.com */ public final class DataTools { // -- Constants -- // -- Static fields -- // -- Constructor -- private DataTools() { } // -- Data reading -- /** Reads the contents of the given file into a string. */ public static String readFile(String id) throws IOException { RandomAccessInputStream in = new RandomAccessInputStream(id); long idLen = in.length(); if (idLen > Integer.MAX_VALUE) { throw new IOException("File too large"); } int len = (int) idLen; String data = in.readString(len); in.close(); return data; } // -- Word decoding - bytes to primitive types -- /** * Translates up to the first len bytes of a byte array beyond the given * offset to a short. If there are fewer than len bytes available, * 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 available * 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 available, 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 len bytes available, * 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 |= 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 available, * 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 available, 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 len bytes available, * 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 available, * 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 available, 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 len bytes available, * 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 |= 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 available, * 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 available, 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 float. If there are fewer than len bytes available, * the MSBs are all assumed to be zero (regardless of endianness). */ public static float bytesToFloat(byte[] bytes, int off, int len, boolean little) { return Float.intBitsToFloat(bytesToInt(bytes, off, len, little)); } /** * Translates up to the first 4 bytes of a byte array beyond a given * offset to a float. If there are fewer than 4 bytes available, * the MSBs are all assumed to be zero (regardless of endianness). */ public static float bytesToFloat(byte[] bytes, int off, boolean little) { return bytesToFloat(bytes, off, 4, little); } /** * Translates up to the first 4 bytes of a byte array to a float. * If there are fewer than 4 bytes available, the MSBs are all * assumed to be zero (regardless of endianness). */ public static float bytesToFloat(byte[] bytes, boolean little) { return bytesToFloat(bytes, 0, 4, little); } /** * Translates up to the first len bytes of a byte array beyond a given * offset to a float. If there are fewer than len bytes available, * the MSBs are all assumed to be zero (regardless of endianness). */ public static float bytesToFloat(short[] bytes, int off, int len, boolean little) { return Float.intBitsToFloat(bytesToInt(bytes, off, len, little)); } /** * Translates up to the first 4 bytes of a byte array beyond a given * offset to a float. If there are fewer than 4 bytes available, * the MSBs are all assumed to be zero (regardless of endianness). */ public static float bytesToFloat(short[] bytes, int off, boolean little) { return bytesToFloat(bytes, off, 4, little); } /** * Translates up to the first 4 bytes of a byte array to a float. * If there are fewer than 4 bytes available, the MSBs are all * assumed to be zero (regardless of endianness). */ public static float bytesToFloat(short[] bytes, boolean little) { return bytesToFloat(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 len bytes available, * 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 available, * 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 available, 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 len bytes available, * 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 available, * 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 available, 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); } /** * Translates up to the first len bytes of a byte array beyond the given * offset to a double. If there are fewer than len bytes available, * the MSBs are all assumed to be zero (regardless of endianness). */ public static double bytesToDouble(byte[] bytes, int off, int len, boolean little) { return Double.longBitsToDouble(bytesToLong(bytes, off, len, little)); } /** * Translates up to the first 8 bytes of a byte array beyond the given * offset to a double. If there are fewer than 8 bytes available, * the MSBs are all assumed to be zero (regardless of endianness). */ public static double bytesToDouble(byte[] bytes, int off, boolean little) { return bytesToDouble(bytes, off, 8, little); } /** * Translates up to the first 8 bytes of a byte array to a double. * If there are fewer than 8 bytes available, the MSBs are all * assumed to be zero (regardless of endianness). */ public static double bytesToDouble(byte[] bytes, boolean little) { return bytesToDouble(bytes, 0, 8, little); } /** * Translates up to the first len bytes of a byte array beyond the given * offset to a double. If there are fewer than len bytes available, * the MSBs are all assumed to be zero (regardless of endianness). */ public static double bytesToDouble(short[] bytes, int off, int len, boolean little) { return Double.longBitsToDouble(bytesToLong(bytes, off, len, little)); } /** * Translates up to the first 8 bytes of a byte array beyond the given * offset to a double. If there are fewer than 8 bytes available, * the MSBs are all assumed to be zero (regardless of endianness). */ public static double bytesToDouble(short[] bytes, int off, boolean little) { return bytesToDouble(bytes, off, 8, little); } /** * Translates up to the first 8 bytes of a byte array to a double. * If there are fewer than 8 bytes available, the MSBs are all * assumed to be zero (regardless of endianness). */ public static double bytesToDouble(short[] bytes, boolean little) { return bytesToDouble(bytes, 0, 8, little); } /** Translates the given byte array into a String of hexadecimal digits. */ public static String bytesToHex(byte[] b) { StringBuffer sb = new StringBuffer(); for (int i=0; i<b.length; i++) { String a = Integer.toHexString(b[i] & 0xff); if (a.length() == 1) sb.append("0"); sb.append(a); } return sb.toString(); } /** Normalize the decimal separator for the user's locale. */ public static String sanitizeDouble(String value) { value = value.replaceAll("[^0-9,\\.]", ""); char separator = new DecimalFormatSymbols().getDecimalSeparator(); char usedSeparator = separator == '.' ? ',' : '.'; value = value.replace(usedSeparator, separator); try { Double.parseDouble(value); } catch (Exception e) { value = value.replace(separator, usedSeparator); } return value; } // -- Word decoding - primitive types to bytes -- /** Translates the short value into an array of two bytes. */ public static byte[] shortToBytes(short value, boolean little) { byte[] v = new byte[2]; unpackBytes(value, v, 0, 2, little); return v; } /** Translates the int value into an array of four bytes. */ public static byte[] intToBytes(int value, boolean little) { byte[] v = new byte[4]; unpackBytes(value, v, 0, 4, little); return v; } /** Translates the float value into an array of four bytes. */ public static byte[] floatToBytes(float value, boolean little) { byte[] v = new byte[4]; unpackBytes(Float.floatToIntBits(value), v, 0, 4, little); return v; } /** Translates the long value into an array of eight bytes. */ public static byte[] longToBytes(long value, boolean little) { byte[] v = new byte[8]; unpackBytes(value, v, 0, 8, little); return v; } /** Translates the double value into an array of eight bytes. */ public static byte[] doubleToBytes(double value, boolean little) { byte[] v = new byte[8]; unpackBytes(Double.doubleToLongBits(value), v, 0, 8, little); return v; } /** Translates an array of short values into an array of byte values. */ public static byte[] shortsToBytes(short[] values, boolean little) { byte[] v = new byte[values.length * 2]; for (int i=0; i<values.length; i++) { unpackBytes(values[i], v, i * 2, 2, little); } return v; } /** Translates an array of int values into an array of byte values. */ public static byte[] intsToBytes(int[] values, boolean little) { byte[] v = new byte[values.length * 4]; for (int i=0; i<values.length; i++) { unpackBytes(values[i], v, i * 4, 4, little); } return v; } /** Translates an array of float values into an array of byte values. */ public static byte[] floatsToBytes(float[] values, boolean little) { byte[] v = new byte[values.length * 4]; for (int i=0; i<values.length; i++) { unpackBytes(Float.floatToIntBits(values[i]), v, i * 4, 4, little); } return v; } /** Translates an array of long values into an array of byte values. */ public static byte[] longsToBytes(long[] values, boolean little) { byte[] v = new byte[values.length * 8]; for (int i=0; i<values.length; i++) { unpackBytes(values[i], v, i * 8, 8, little); } return v; } /** Translates an array of double values into an array of byte values. */ public static byte[] doublesToBytes(double[] values, boolean little) { byte[] v = new byte[values.length * 8]; for (int i=0; i<values.length; i++) { unpackBytes(Double.doubleToLongBits(values[i]), v, i * 8, 8, little); } return v; } /** * Translates nBytes of the given long and places the result in the * given byte array. * * @throws IllegalArgumentException * if the specified indices fall outside the buffer */ public static void unpackBytes(long value, byte[] buf, int ndx, int nBytes, boolean little) { if (buf.length < ndx + nBytes) { throw new IllegalArgumentException("Invalid indices: buf.length=" + buf.length + ", ndx=" + ndx + ", nBytes=" + nBytes); } if (little) { for (int i=0; i<nBytes; i++) { buf[ndx + i] = (byte) ((value >> (8 * i)) & 0xff); } } else { for (int i=0; i<nBytes; i++) { buf[ndx + i] = (byte) ((value >> (8 * (nBytes - i - 1))) & 0xff); } } } /** * Convert a byte array to the appropriate 1D 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] = bytesToFloat(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] = bytesToDouble(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; } /** * Convert a byte array to the appropriate 2D 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. * @param height The height of the output primitive array (2nd dim length). * * @return a 2D primitive array of appropriate type, * dimensioned [height][b.length / (bpp * height)] * * @throws IllegalArgumentException if input byte array does not divide * evenly into height pieces */ public static Object makeDataArray2D(byte[] b, int bpp, boolean fp, boolean little, int height) { if (b.length % (bpp * height) != 0) { } final int width = b.length / (bpp * height); if (bpp == 1) { byte[][] b2 = new byte[height][width]; for (int y=0; y<height; y++) { int index = width * y; System.arraycopy(b, index, b2[y], 0, width); } return b2; } else if (bpp == 2) { short[][] s = new short[height][width]; for (int y=0; y<height; y++) { for (int x=0; x<width; x++) { int index = 2 * (width * y + x); s[y][x] = bytesToShort(b, index, 2, little); } } return s; } else if (bpp == 4 && fp) { float[][] f = new float[height][width]; for (int y=0; y<height; y++) { for (int x=0; x<width; x++) { int index = 4 * (width * y + x); f[y][x] = bytesToFloat(b, index, 4, little); } } return f; } else if (bpp == 4) { int[][] i = new int[height][width]; for (int y=0; y<height; y++) { for (int x=0; x<width; x++) { int index = 4 * (width * y + x); i[y][x] = bytesToInt(b, index, 4, little); } } return i; } else if (bpp == 8 && fp) { double[][] d = new double[height][width]; for (int y=0; y<height; y++) { for (int x=0; x<width; x++) { int index = 8 * (width * y + x); d[y][x] = bytesToDouble(b, index, 8, little); } } return d; } else if (bpp == 8) { long[][] l = new long[height][width]; for (int y=0; y<height; y++) { for (int x=0; x<width; x++) { int index = 8 * (width * y + x); l[y][x] = bytesToLong(b, index, 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 (swap((short) x) << 16) | (swap((short) (x >> 16)) & 0xFFFF); } public static long swap(long x) { return ((long) swap((int) x) << 32) | (swap((int) (x >> 32)) & 0xFFFFFFFFL); } public static float swap(float x) { return Float.intBitsToFloat(swap(Float.floatToIntBits(x))); } public static double swap(double x) { return Double.longBitsToDouble(swap(Double.doubleToLongBits(x))); } // -- Strings -- /** Remove null bytes from a string. */ public static String stripString(String toStrip) { StringBuffer s = new StringBuffer(); for (int i=0; i<toStrip.length(); i++) { if (toStrip.charAt(i) != 0) { s.append(toStrip.charAt(i)); } } return s.toString().trim(); } /** 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, n1); String sub2 = s2.substring(slash2 + 1, n2); return sub1.equals(sub2) || sub1.startsWith(sub2) || sub2.startsWith(sub1); } /** Remove unprintable characters from the given string. */ public static String sanitize(String s) { if (s == null) return null; StringBuffer buf = new StringBuffer(s); for (int i=0; i<buf.length(); i++) { char c = buf.charAt(i); if (c != '\t' && c != '\n' && Character.isISOControl(c)) { buf = buf.deleteCharAt(i--); } } return buf.toString(); } // -- Normalization -- /** * 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]; // determine the finite 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] == Float.POSITIVE_INFINITY || data[i] == Float.NEGATIVE_INFINITY) { continue; } if (data[i] < min) min = data[i]; if (data[i] > max) max = data[i]; } // normalize infinity values for (int i=0; i<data.length; i++) { if (data[i] == Float.POSITIVE_INFINITY) data[i] = max; else if (data[i] == Float.NEGATIVE_INFINITY) data[i] = min; } // now normalize; min => 0.0, max => 1.0 float range = max - min; for (int i=0; i<rtn.length; i++) { rtn[i] = (data[i] - min) / range; } return rtn; } /** * Normalize the given double array so that the minimum value maps to 0.0 * and the maximum value maps to 1.0. */ public static double[] normalizeDoubles(double[] data) { double[] rtn = new double[data.length]; // determine the finite min and max values double min = Double.MAX_VALUE; double max = Double.MIN_VALUE; for (int i=0; i<data.length; i++) { if (data[i] == Double.POSITIVE_INFINITY || data[i] == Double.NEGATIVE_INFINITY) { continue; } if (data[i] < min) min = data[i]; if (data[i] > max) max = data[i]; } // normalize infinity values for (int i=0; i<data.length; i++) { if (data[i] == Double.POSITIVE_INFINITY) data[i] = max; else if (data[i] == Double.NEGATIVE_INFINITY) data[i] = min; } // now normalize; min => 0.0, max => 1.0 double range = max - min; for (int i=0; i<rtn.length; i++) { rtn[i] = (data[i] - min) / range; } return rtn; } // -- Array handling -- /** * Allocates a 1-dimensional byte array matching the product of the given * sizes. * * @param sizes list of sizes from which to allocate the array * @return a byte array of the appropriate size * @throws IllegalArgumentException if the total size exceeds 2GB, which is * the maximum size of an array in Java; or if any size argument is * zero or negative */ public static byte[] allocate(int... sizes) throws IllegalArgumentException { if (sizes == null) return null; if (sizes.length == 0) return new byte[0]; int total = safeMultiply32(sizes); return new byte[total]; } /** * Checks that the product of the given sizes does not exceed the 32-bit * integer limit (i.e., {@link Integer#MAX_VALUE}). * * @param sizes list of sizes from which to compute the product * @return the product of the given sizes * @throws IllegalArgumentException if the total size exceeds 2GiB, which is * the maximum size of an int in Java; or if any size argument is * zero or negative */ public static int safeMultiply32(int... sizes) throws IllegalArgumentException { if (sizes.length == 0) return 0; long total = 1; for (int size : sizes) { if (size < 1) { throw new IllegalArgumentException("Invalid array size: " + sizeAsProduct(sizes)); } total *= size; if (total > Integer.MAX_VALUE) { throw new IllegalArgumentException("Array size too large: " + sizeAsProduct(sizes)); } } // NB: The downcast to int is safe here, due to the checks above. return (int) total; } /** * Checks that the product of the given sizes does not exceed the 64-bit * integer limit (i.e., {@link Long#MAX_VALUE}). * * @param sizes list of sizes from which to compute the product * @return the product of the given sizes * @throws IllegalArgumentException if the total size exceeds 8EiB, which is * the maximum size of a long in Java; or if any size argument is * zero or negative */ public static long safeMultiply64(long... sizes) throws IllegalArgumentException { if (sizes.length == 0) return 0; long total = 1; for (long size : sizes) { if (size < 1) { throw new IllegalArgumentException("Invalid array size: " + sizeAsProduct(sizes)); } if (willOverflow(total, size)) { throw new IllegalArgumentException("Array size too large: " + sizeAsProduct(sizes)); } total *= size; } return total; } /** Returns true if the given value is contained in the given array. */ public static boolean containsValue(int[] array, int value) { return indexOf(array, value) != -1; } /** * Returns the index of the first occurrence of the given value in the given * array. If the value is not in the array, returns -1. */ public static int indexOf(int[] array, int value) { for (int i=0; i<array.length; i++) { if (array[i] == value) return i; } return -1; } /** * Returns the index of the first occurrence of the given value in the given * Object array. If the value is not in the array, returns -1. */ public static int indexOf(Object[] array, Object value) { for (int i=0; i<array.length; i++) { if (value == null) { if (array[i] == null) return i; } else if (value.equals(array[i])) return i; } return -1; } // -- Signed data conversion -- public static byte[] makeSigned(byte[] b) { for (int i=0; i<b.length; i++) { b[i] = (byte) (b[i] + 128); } return b; } public static short[] makeSigned(short[] s) { for (int i=0; i<s.length; i++) { s[i] = (short) (s[i] + 32768); } return s; } public static int[] makeSigned(int[] i) { for (int j=0; j<i.length; j++) { i[j] = (int) (i[j] + 2147483648L); } return i; } // -- Helper methods -- private static String sizeAsProduct(int... sizes) { StringBuilder sb = new StringBuilder(); boolean first = true; for (int size : sizes) { if (first) first = false; else sb.append(" x "); sb.append(size); } return sb.toString(); } private static String sizeAsProduct(long... sizes) { StringBuilder sb = new StringBuilder(); boolean first = true; for (long size : sizes) { if (first) first = false; else sb.append(" x "); sb.append(size); } return sb.toString(); } private static boolean willOverflow(long v1, long v2) { return Long.MAX_VALUE / v1 < v2; } }