This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-imaging.git
commit 74d4c28893fefe08c6e24cbd63537051c745093d Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Sat May 13 17:50:42 2023 -0400 Smarter allocation checking --- .../apache/commons/imaging/common/Allocator.java | 117 +++++++++++++++++---- .../commons/imaging/common/ByteConversions.java | 2 +- .../apache/commons/imaging/common/PackBits.java | 2 +- .../apache/commons/imaging/common/ZlibDeflate.java | 2 +- .../imaging/common/mylzw/MyLzwCompressor.java | 2 +- .../imaging/common/mylzw/MyLzwDecompressor.java | 2 +- .../imaging/formats/dcx/DcxImageParser.java | 7 +- .../imaging/formats/ico/IcoImageParser.java | 2 +- .../formats/tiff/fieldtypes/FieldTypeDouble.java | 2 +- .../formats/tiff/fieldtypes/FieldTypeShort.java | 2 +- .../imaging/formats/xpm/XpmImageParser.java | 2 +- .../imaging/test/util/PrintShallowObjectSizes.java | 6 +- 12 files changed, 111 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/apache/commons/imaging/common/Allocator.java b/src/main/java/org/apache/commons/imaging/common/Allocator.java index 979adeb4..3316975d 100644 --- a/src/main/java/org/apache/commons/imaging/common/Allocator.java +++ b/src/main/java/org/apache/commons/imaging/common/Allocator.java @@ -38,7 +38,7 @@ public class Allocator { /** * Allocates an Object of type T of the requested size. * - * @param <T> The return array type + * @param <T> The return array type * @param request The requested size. * @param factory The array factory. * @return a new byte array. @@ -52,28 +52,30 @@ public class Allocator { /** * Allocates an array of type T of the requested size. * - * @param <T> The return array type - * @param request The requested size. - * @param factory The array factory. - * @param shallowByteSize The shallow byte size. + * @param <T> The return array type + * @param request The requested size. + * @param factory The array factory. + * @param eltShallowByteSize The shallow byte size of an element. * @return a new byte array. * @throws AllocationRequestException Thrown when the request exceeds the limit. * @see #check(int) */ - public static <T> T[] array(final int request, final IntFunction<T[]> factory, final int shallowByteSize) { - return factory.apply(check(request * shallowByteSize)); + public static <T> T[] array(final int request, final IntFunction<T[]> factory, final int eltShallowByteSize) { + check(request * eltShallowByteSize); + return factory.apply(request); } /** * Allocates an Object array of type T of the requested size. * - * @param <T> The return array type + * @param <T> The return array type * @param request The requested size. * @return a new byte array. * @throws AllocationRequestException Thrown when the request exceeds the limit. * @see #check(int) */ public static <T> ArrayList<T> arrayList(final int request) { + check(24 + request * 4); // 4 bytes per element return apply(request, ArrayList::new); } @@ -83,10 +85,10 @@ public class Allocator { * @param request The requested size. * @return a new byte array. * @throws AllocationRequestException Thrown when the request exceeds the limit. - * @see #check(int) + * @see #check(int, int) */ public static byte[] byteArray(final int request) { - return new byte[check(request)]; + return new byte[checkByteArray(request)]; } /** @@ -95,10 +97,22 @@ public class Allocator { * @param request The requested size is cast down to an int. * @return a new byte array. * @throws AllocationRequestException Thrown when the request exceeds the limit. - * @see #check(int) + * @see #check(int, int) */ public static byte[] byteArray(final long request) { - return new byte[check(request)]; + return new byte[check(request, Byte.BYTES)]; + } + + /** + * Allocates a char array of the requested size. + * + * @param request The requested size. + * @return a new char array. + * @throws AllocationRequestException Thrown when the request exceeds the limit. + * @see #check(int, int) + */ + public static char[] charArray(final int request) { + return new char[check(request, Character.BYTES)]; } /** @@ -114,7 +128,7 @@ public class Allocator { */ public static int check(final int request) { if (request > LIMIT) { - throw new AllocationRequestException(DEFAULT, request); + throw new AllocationRequestException(LIMIT, request); } return request; } @@ -126,15 +140,48 @@ public class Allocator { * "org.apache.commons.imaging.common.mylzw.AllocationChecker". * </p> * - * @param request the allocation request is cast down to an int. + * @param request an allocation request�count. + * @param elementSize The element size. * @return the request. * @throws AllocationRequestException Thrown when the request exceeds the limit. + * @throws ArithmeticException if the result overflows an int. */ - public static int check(final long request) { + public static int check(final int request, final int elementSize) { + final int multiplyExact = Math.multiplyExact(request, elementSize); + if (multiplyExact > LIMIT) { + throw new AllocationRequestException(LIMIT, request); + } + return request; + } + + /** + * Checks a request for meeting allocation limits. + * <p> + * The default limit is {@value #DEFAULT}, override with the system property + * "org.apache.commons.imaging.common.mylzw.AllocationChecker". + * </p> + * + * @param request an allocation request�count is cast down to an int. + * @param elementSize The element size. + * @return the request. + * @throws AllocationRequestException Thrown when the request exceeds the limit. + * @throws ArithmeticException if the result overflows an int. + */ + public static int check(final long request, final int elementSize) { if (request > Integer.MAX_VALUE) { - throw new AllocationRequestException(DEFAULT, request); + throw new AllocationRequestException(LIMIT, request); } - return check((int) request); + return check((int) request, elementSize); + } + + /** + * Checks that allocating a byte array of the requested size is within the limit. + * + * @param request The byte array size. + * @return The input request. + */ + public static int checkByteArray(final int request) { + return check(request, Byte.BYTES); } /** @@ -143,10 +190,10 @@ public class Allocator { * @param request The requested size. * @return a new double array. * @throws AllocationRequestException Thrown when the request exceeds the limit. - * @see #check(int) + * @see #check(int, int) */ public static double[] doubleArray(final int request) { - return new double[check(request)]; + return new double[check(request, Double.BYTES)]; } /** @@ -155,10 +202,10 @@ public class Allocator { * @param request The requested size. * @return a new float array. * @throws AllocationRequestException Thrown when the request exceeds the limit. - * @see #check(int) + * @see #check(int, int) */ public static float[] floatArray(final int request) { - return new float[check(request)]; + return new float[check(request, Float.BYTES)]; } /** @@ -167,10 +214,34 @@ public class Allocator { * @param request The requested size. * @return a new int array. * @throws AllocationRequestException Thrown when the request exceeds the limit. - * @see #check(int) + * @see #check(int, int) */ public static int[] intArray(final int request) { - return new int[check(request)]; + return new int[check(request, Integer.BYTES)]; + } + + /** + * Allocates a long array of the requested size. + * + * @param request The requested size. + * @return a new long array. + * @throws AllocationRequestException Thrown when the request exceeds the limit. + * @see #check(int, int) + */ + public static long[] longArray(final int request) { + return new long[check(request, Long.BYTES)]; + } + + /** + * Allocates a short array of the requested size. + * + * @param request The requested size. + * @return a new short array. + * @throws AllocationRequestException Thrown when the request exceeds the limit. + * @see #check(int, int) + */ + public static short[] shortArray(final int request) { + return new short[check(request, Short.BYTES)]; } } diff --git a/src/main/java/org/apache/commons/imaging/common/ByteConversions.java b/src/main/java/org/apache/commons/imaging/common/ByteConversions.java index 14fb2963..2cc02058 100644 --- a/src/main/java/org/apache/commons/imaging/common/ByteConversions.java +++ b/src/main/java/org/apache/commons/imaging/common/ByteConversions.java @@ -368,7 +368,7 @@ public final class ByteConversions { private static short[] toShorts(final byte[] bytes, final int offset, final int length, final ByteOrder byteOrder) { - final short[] result = new short[Allocator.check(length / 2)]; + final short[] result = Allocator.shortArray(length / 2); for (int i = 0; i < result.length; i++) { result[i] = toShort(bytes, offset + 2 * i, byteOrder); } diff --git a/src/main/java/org/apache/commons/imaging/common/PackBits.java b/src/main/java/org/apache/commons/imaging/common/PackBits.java index 1ed30ca1..34512720 100644 --- a/src/main/java/org/apache/commons/imaging/common/PackBits.java +++ b/src/main/java/org/apache/commons/imaging/common/PackBits.java @@ -26,7 +26,7 @@ public class PackBits { public byte[] compress(final byte[] bytes) throws IOException { // max length 1 extra byte for every 128 - try (UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream(Allocator.check(bytes.length * 2))) { + try (UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream(Allocator.checkByteArray(bytes.length * 2))) { int ptr = 0; while (ptr < bytes.length) { int dup = findNextDuplicate(bytes, ptr); diff --git a/src/main/java/org/apache/commons/imaging/common/ZlibDeflate.java b/src/main/java/org/apache/commons/imaging/common/ZlibDeflate.java index 68b72457..fb010035 100644 --- a/src/main/java/org/apache/commons/imaging/common/ZlibDeflate.java +++ b/src/main/java/org/apache/commons/imaging/common/ZlibDeflate.java @@ -48,7 +48,7 @@ public class ZlibDeflate { * @see DeflaterOutputStream */ public static byte[] compress(final byte[] bytes) throws ImageWriteException { - final ByteArrayOutputStream out = new ByteArrayOutputStream(Allocator.check(bytes.length / 2)); + final ByteArrayOutputStream out = new ByteArrayOutputStream(Allocator.checkByteArray(bytes.length / 2)); try (DeflaterOutputStream compressOut = new DeflaterOutputStream(out)) { compressOut.write(bytes); } catch (final IOException e) { diff --git a/src/main/java/org/apache/commons/imaging/common/mylzw/MyLzwCompressor.java b/src/main/java/org/apache/commons/imaging/common/mylzw/MyLzwCompressor.java index 91b2aa42..30bb6baa 100644 --- a/src/main/java/org/apache/commons/imaging/common/mylzw/MyLzwCompressor.java +++ b/src/main/java/org/apache/commons/imaging/common/mylzw/MyLzwCompressor.java @@ -175,7 +175,7 @@ public class MyLzwCompressor { } public byte[] compress(final byte[] bytes) throws IOException { - final ByteArrayOutputStream baos = new ByteArrayOutputStream(Allocator.check(bytes.length)); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(Allocator.checkByteArray(bytes.length)); final MyBitOutputStream bos = new MyBitOutputStream(baos, byteOrder); initializeStringTable(); diff --git a/src/main/java/org/apache/commons/imaging/common/mylzw/MyLzwDecompressor.java b/src/main/java/org/apache/commons/imaging/common/mylzw/MyLzwDecompressor.java index 3494158d..47048f55 100644 --- a/src/main/java/org/apache/commons/imaging/common/mylzw/MyLzwDecompressor.java +++ b/src/main/java/org/apache/commons/imaging/common/mylzw/MyLzwDecompressor.java @@ -112,7 +112,7 @@ public final class MyLzwDecompressor { mbis.setTiffLZWMode(); } - final ByteArrayOutputStream baos = new ByteArrayOutputStream(Allocator.check(expectedLength)); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(Allocator.checkByteArray(expectedLength)); clearTable(); diff --git a/src/main/java/org/apache/commons/imaging/formats/dcx/DcxImageParser.java b/src/main/java/org/apache/commons/imaging/formats/dcx/DcxImageParser.java index d83a8599..532fd5e3 100644 --- a/src/main/java/org/apache/commons/imaging/formats/dcx/DcxImageParser.java +++ b/src/main/java/org/apache/commons/imaging/formats/dcx/DcxImageParser.java @@ -159,8 +159,9 @@ public class DcxImageParser extends ImageParser<PcxImagingParameters> { throws ImageReadException, IOException { try (InputStream is = byteSource.getInputStream()) { final int id = read4Bytes("Id", is, "Not a Valid DCX File", getByteOrder()); - final List<Long> pageTable = Allocator.arrayList(1024); - for (int i = 0; i < 1024; i++) { + final int size = 1024; + final List<Long> pageTable = Allocator.arrayList(size); + for (int i = 0; i < size; i++) { final long pageOffset = 0xFFFFffffL & read4Bytes("PageTable", is, "Not a Valid DCX File", getByteOrder()); if (pageOffset == 0) { break; @@ -171,7 +172,7 @@ public class DcxImageParser extends ImageParser<PcxImagingParameters> { if (id != DcxHeader.DCX_ID) { throw new ImageReadException("Not a Valid DCX File: file id incorrect"); } - if (pageTable.size() == 1024) { + if (pageTable.size() == size) { throw new ImageReadException("DCX page table not terminated by zero entry"); } diff --git a/src/main/java/org/apache/commons/imaging/formats/ico/IcoImageParser.java b/src/main/java/org/apache/commons/imaging/formats/ico/IcoImageParser.java index aaf16562..fef55082 100644 --- a/src/main/java/org/apache/commons/imaging/formats/ico/IcoImageParser.java +++ b/src/main/java/org/apache/commons/imaging/formats/ico/IcoImageParser.java @@ -437,7 +437,7 @@ public class IcoImageParser extends ImageParser<IcoImagingParameters> { : colorsUsed); final int bitmapSize = 14 + 56 + restOfFile.length; - final ByteArrayOutputStream baos = new ByteArrayOutputStream(Allocator.check(bitmapSize)); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(Allocator.checkByteArray(bitmapSize)); try (BinaryOutputStream bos = BinaryOutputStream.littleEndian(baos)) { bos.write('B'); bos.write('M'); diff --git a/src/main/java/org/apache/commons/imaging/formats/tiff/fieldtypes/FieldTypeDouble.java b/src/main/java/org/apache/commons/imaging/formats/tiff/fieldtypes/FieldTypeDouble.java index 65a143fa..5ae7b503 100644 --- a/src/main/java/org/apache/commons/imaging/formats/tiff/fieldtypes/FieldTypeDouble.java +++ b/src/main/java/org/apache/commons/imaging/formats/tiff/fieldtypes/FieldTypeDouble.java @@ -49,7 +49,7 @@ public class FieldTypeDouble extends FieldType { if (!(o instanceof Double[])) { throw new ImageWriteException("Invalid data", o); } - final double[] values = new double[Allocator.check(((Double[]) o).length)]; + final double[] values = Allocator.doubleArray(((Double[]) o).length); Arrays.setAll(values, i -> ((Double[]) o)[i].doubleValue()); return ByteConversions.toBytes(values, byteOrder); } diff --git a/src/main/java/org/apache/commons/imaging/formats/tiff/fieldtypes/FieldTypeShort.java b/src/main/java/org/apache/commons/imaging/formats/tiff/fieldtypes/FieldTypeShort.java index ee661c2a..38e3697b 100644 --- a/src/main/java/org/apache/commons/imaging/formats/tiff/fieldtypes/FieldTypeShort.java +++ b/src/main/java/org/apache/commons/imaging/formats/tiff/fieldtypes/FieldTypeShort.java @@ -50,7 +50,7 @@ public class FieldTypeShort extends FieldType { throw new ImageWriteException("Invalid data", o); } final Short[] numbers = (Short[]) o; - final short[] values = new short[Allocator.check(numbers.length)]; + final short[] values = Allocator.shortArray(numbers.length); for (int i = 0; i < values.length; i++) { values[i] = numbers[i].shortValue(); } diff --git a/src/main/java/org/apache/commons/imaging/formats/xpm/XpmImageParser.java b/src/main/java/org/apache/commons/imaging/formats/xpm/XpmImageParser.java index ee302fbc..cec09f22 100644 --- a/src/main/java/org/apache/commons/imaging/formats/xpm/XpmImageParser.java +++ b/src/main/java/org/apache/commons/imaging/formats/xpm/XpmImageParser.java @@ -638,7 +638,7 @@ public class XpmImageParser extends ImageParser<XpmImagingParameters> { private String toColor(final int color) { final String hex = Integer.toHexString(color); if (hex.length() < 6) { - final char[] zeroes = new char[Allocator.check(6 - hex.length())]; + final char[] zeroes = Allocator.charArray(6 - hex.length()); Arrays.fill(zeroes, '0'); return "#" + new String(zeroes) + hex; } diff --git a/src/test/java/org/apache/commons/imaging/test/util/PrintShallowObjectSizes.java b/src/test/java/org/apache/commons/imaging/test/util/PrintShallowObjectSizes.java index 0f6c2685..e44deb13 100644 --- a/src/test/java/org/apache/commons/imaging/test/util/PrintShallowObjectSizes.java +++ b/src/test/java/org/apache/commons/imaging/test/util/PrintShallowObjectSizes.java @@ -17,6 +17,8 @@ package org.apache.commons.imaging.test.util; +import java.util.ArrayList; + import org.apache.commons.imaging.common.RationalNumber; import org.apache.commons.imaging.icc.IccTag; import org.openjdk.jol.info.ClassLayout; @@ -31,13 +33,13 @@ public class PrintShallowObjectSizes { new PrintShallowObjectSizes().go(String.class, org.apache.commons.imaging.formats.jpeg.segments.SofnSegment.Component.class, org.apache.commons.imaging.formats.jpeg.segments.SosSegment.Component.class, RationalNumber.class, - IccTag.class); + IccTag.class, ArrayList.class); } public void go(final Class<?>... classes) { for (final Class<?> cls : classes) { final ClassLayout classLayout = ClassLayout.parseClass(cls); - // System.out.println(classLayout.toPrintable()); + System.out.println(classLayout.toPrintable()); System.out.printf("%s shallow size = %,d bytes%n", cls, classLayout.instanceSize()); }