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

Reply via email to