Skip to content

Commit 568b2b2

Browse files
committed
Migrate DataTools methods from SCIFIO/Bio-Formats
The io.scif.common.DataTools class (originally from Bio-Formats at loci.common.DataTools) contains many useful methods for working with primitive numerical types, and arrays of those types. In particular, it includes methods for converting between types, endianness, and signedness. These utility methods will continue to be extremely useful for the various DataHandle implementations, especially for proper support for both big- and little-endian byte ordering. The class was split as follows: * org.scijava.util.Bytes - primitive numeric methods * org.scijava.util.StringUtils - utility methods for strings * org.scijava.util.ArrayUtils - utility methods for arrays Regarding provenance of the code: I originally wrote much of it for the Bio-Formats library; Melissa Linkert wrote a lot as well, with additional contributions by Chris Allan and probably others on the OME team. The relevant code is all BSD-2 licensed; I have added an appropriate extra legal notice at the top of each affected file.
1 parent 4286ec1 commit 568b2b2

File tree

3 files changed

+1223
-0
lines changed

3 files changed

+1223
-0
lines changed

src/main/java/org/scijava/util/ArrayUtils.java

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,35 @@
2929
* #L%
3030
*/
3131

32+
// Portions of this class were derived from the loci.common.DataTools class of
33+
// the Bio-Formats library, licensed according to Simplified BSD, as follows:
34+
//
35+
// Copyright (C) 2005 - 2015 Open Microscopy Environment:
36+
// - Board of Regents of the University of Wisconsin-Madison
37+
// - Glencoe Software, Inc.
38+
// - University of Dundee
39+
//
40+
// Redistribution and use in source and binary forms, with or without
41+
// modification, are permitted provided that the following conditions are met:
42+
//
43+
// 1. Redistributions of source code must retain the above copyright notice,
44+
// this list of conditions and the following disclaimer.
45+
// 2. Redistributions in binary form must reproduce the above copyright notice,
46+
// this list of conditions and the following disclaimer in the documentation
47+
// and/or other materials provided with the distribution.
48+
//
49+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
50+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52+
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
53+
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
54+
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
55+
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
56+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
57+
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
58+
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
59+
// POSSIBILITY OF SUCH DAMAGE.
60+
3261
package org.scijava.util;
3362

3463
import java.util.Collection;
@@ -38,6 +67,9 @@
3867
* Utility class for creating and manipulating {@link PrimitiveArray} instances.
3968
*
4069
* @author Mark Hiner
70+
* @author Curtis Rueden
71+
* @author Melissa Linkert
72+
* @author Chris Allan
4173
*/
4274
public final class ArrayUtils {
4375

@@ -91,4 +123,246 @@ public static Collection<?> toCollection(final Object value) {
91123
return list;
92124
}
93125

126+
/**
127+
* Allocates a 1-dimensional byte array matching the product of the given
128+
* sizes.
129+
*
130+
* @param sizes list of sizes from which to allocate the array
131+
* @return a byte array of the appropriate size
132+
* @throws IllegalArgumentException if the total size exceeds 2GB, which is
133+
* the maximum size of an array in Java; or if any size argument is
134+
* zero or negative
135+
*/
136+
public static byte[] allocate(final long... sizes)
137+
throws IllegalArgumentException
138+
{
139+
if (sizes == null) return null;
140+
if (sizes.length == 0) return new byte[0];
141+
final int total = safeMultiply32(sizes);
142+
return new byte[total];
143+
}
144+
145+
/**
146+
* Checks that the product of the given sizes does not exceed the 32-bit
147+
* integer limit (i.e., {@link Integer#MAX_VALUE}).
148+
*
149+
* @param sizes list of sizes from which to compute the product
150+
* @return the product of the given sizes
151+
* @throws IllegalArgumentException if the total size exceeds 2GiB, which is
152+
* the maximum size of an int in Java; or if any size argument is
153+
* zero or negative
154+
*/
155+
public static int safeMultiply32(final long... sizes)
156+
throws IllegalArgumentException
157+
{
158+
if (sizes.length == 0) return 0;
159+
long total = 1;
160+
for (final long size : sizes) {
161+
if (size < 1) {
162+
throw new IllegalArgumentException("Invalid array size: " +
163+
sizeAsProduct(sizes));
164+
}
165+
total *= size;
166+
if (total > Integer.MAX_VALUE) {
167+
throw new IllegalArgumentException("Array size too large: " +
168+
sizeAsProduct(sizes));
169+
}
170+
}
171+
// NB: The downcast to int is safe here, due to the checks above.
172+
return (int) total;
173+
}
174+
175+
/**
176+
* Checks that the product of the given sizes does not exceed the 64-bit
177+
* integer limit (i.e., {@link Long#MAX_VALUE}).
178+
*
179+
* @param sizes list of sizes from which to compute the product
180+
* @return the product of the given sizes
181+
* @throws IllegalArgumentException if the total size exceeds 8EiB, which is
182+
* the maximum size of a long in Java; or if any size argument is
183+
* zero or negative
184+
*/
185+
public static long safeMultiply64(final long... sizes)
186+
throws IllegalArgumentException
187+
{
188+
if (sizes.length == 0) return 0;
189+
long total = 1;
190+
for (final long size : sizes) {
191+
if (size < 1) {
192+
throw new IllegalArgumentException("Invalid array size: " +
193+
sizeAsProduct(sizes));
194+
}
195+
if (willOverflow(total, size)) {
196+
throw new IllegalArgumentException("Array size too large: " +
197+
sizeAsProduct(sizes));
198+
}
199+
total *= size;
200+
}
201+
return total;
202+
}
203+
204+
/** Returns true if the given value is contained in the specified array. */
205+
public static boolean contains(final byte[] array, final byte value) {
206+
return indexOf(array, value) != -1;
207+
}
208+
209+
/** Returns true if the given value is contained in the specified array. */
210+
public static boolean contains(final boolean[] array, final boolean value) {
211+
return indexOf(array, value) != -1;
212+
}
213+
214+
/** Returns true if the given value is contained in the specified array. */
215+
public static boolean contains(final char[] array, final char value) {
216+
return indexOf(array, value) != -1;
217+
}
218+
219+
/** Returns true if the given value is contained in the specified array. */
220+
public static boolean contains(final double[] array, final double value) {
221+
return indexOf(array, value) != -1;
222+
}
223+
224+
/** Returns true if the given value is contained in the specified array. */
225+
public static boolean contains(final float[] array, final float value) {
226+
return indexOf(array, value) != -1;
227+
}
228+
229+
/** Returns true if the given value is contained in the specified array. */
230+
public static boolean contains(final int[] array, final int value) {
231+
return indexOf(array, value) != -1;
232+
}
233+
234+
/** Returns true if the given value is contained in the specified array. */
235+
public static boolean contains(final long[] array, final long value) {
236+
return indexOf(array, value) != -1;
237+
}
238+
239+
/** Returns true if the given value is contained in the specified array. */
240+
public static boolean contains(final short[] array, final short value) {
241+
return indexOf(array, value) != -1;
242+
}
243+
244+
/** Returns true if the given value is contained in the specified array. */
245+
public static boolean contains(final Object[] array, final Object value) {
246+
return indexOf(array, value) != -1;
247+
}
248+
249+
/**
250+
* Returns the index of the first occurrence of the given value in the given
251+
* array. If the value is not in the array, returns -1.
252+
*/
253+
public static int indexOf(final boolean[] array, final boolean value) {
254+
for (int i = 0; i < array.length; i++) {
255+
if (array[i] == value) return i;
256+
}
257+
return -1;
258+
}
259+
260+
/**
261+
* Returns the index of the first occurrence of the given value in the given
262+
* array. If the value is not in the array, returns -1.
263+
*/
264+
public static int indexOf(final byte[] array, final byte value) {
265+
for (int i = 0; i < array.length; i++) {
266+
if (array[i] == value) return i;
267+
}
268+
return -1;
269+
}
270+
271+
/**
272+
* Returns the index of the first occurrence of the given value in the given
273+
* array. If the value is not in the array, returns -1.
274+
*/
275+
public static int indexOf(final char[] array, final char value) {
276+
for (int i = 0; i < array.length; i++) {
277+
if (array[i] == value) return i;
278+
}
279+
return -1;
280+
}
281+
282+
/**
283+
* Returns the index of the first occurrence of the given value in the given
284+
* array. If the value is not in the array, returns -1.
285+
*/
286+
public static int indexOf(final double[] array, final double value) {
287+
for (int i = 0; i < array.length; i++) {
288+
if (array[i] == value) return i;
289+
}
290+
return -1;
291+
}
292+
293+
/**
294+
* Returns the index of the first occurrence of the given value in the given
295+
* array. If the value is not in the array, returns -1.
296+
*/
297+
public static int indexOf(final float[] array, final float value) {
298+
for (int i = 0; i < array.length; i++) {
299+
if (array[i] == value) return i;
300+
}
301+
return -1;
302+
}
303+
304+
/**
305+
* Returns the index of the first occurrence of the given value in the given
306+
* array. If the value is not in the array, returns -1.
307+
*/
308+
public static int indexOf(final int[] array, final int value) {
309+
for (int i = 0; i < array.length; i++) {
310+
if (array[i] == value) return i;
311+
}
312+
return -1;
313+
}
314+
315+
/**
316+
* Returns the index of the first occurrence of the given value in the given
317+
* array. If the value is not in the array, returns -1.
318+
*/
319+
public static int indexOf(final long[] array, final long value) {
320+
for (int i = 0; i < array.length; i++) {
321+
if (array[i] == value) return i;
322+
}
323+
return -1;
324+
}
325+
326+
/**
327+
* Returns the index of the first occurrence of the given value in the given
328+
* array. If the value is not in the array, returns -1.
329+
*/
330+
public static int indexOf(final short[] array, final short value) {
331+
for (int i = 0; i < array.length; i++) {
332+
if (array[i] == value) return i;
333+
}
334+
return -1;
335+
}
336+
337+
/**
338+
* Returns the index of the first occurrence of the given value in the given
339+
* Object array. If the value is not in the array, returns -1.
340+
*/
341+
public static int indexOf(final Object[] array, final Object value) {
342+
for (int i = 0; i < array.length; i++) {
343+
if (value == null) {
344+
if (array[i] == null) return i;
345+
}
346+
else if (value.equals(array[i])) return i;
347+
}
348+
return -1;
349+
}
350+
351+
// -- Helper methods --
352+
353+
private static String sizeAsProduct(final long... sizes) {
354+
final StringBuilder sb = new StringBuilder();
355+
boolean first = true;
356+
for (final long size : sizes) {
357+
if (first) first = false;
358+
else sb.append(" x ");
359+
sb.append(size);
360+
}
361+
return sb.toString();
362+
}
363+
364+
private static boolean willOverflow(final long v1, final long v2) {
365+
return Long.MAX_VALUE / v1 < v2;
366+
}
367+
94368
}

0 commit comments

Comments
 (0)