001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.imaging.common; 019 020import java.math.BigInteger; 021import java.util.ArrayList; 022import java.util.function.IntFunction; 023 024/** 025 * Checks inputs for meeting allocation limits and allocates arrays. 026 */ 027public class Allocator { 028 029 private static final String CANONICAL_NAME = Allocator.class.getCanonicalName(); 030 031 /** One GB. */ 032 private static final int DEFAULT = 1_073_741_824; 033 private static final int LIMIT; 034 035 static { 036 LIMIT = Integer.getInteger(CANONICAL_NAME, DEFAULT); 037 } 038 039 /** 040 * Allocates an Object of type T of the requested size. 041 * 042 * @param <T> The return array type 043 * @param request The requested size. 044 * @param factory The array factory. 045 * @return a new byte array. 046 * @throws AllocationRequestException Thrown when the request exceeds the limit. 047 * @see #check(int) 048 */ 049 public static <T> T apply(final int request, final IntFunction<T> factory) { 050 return factory.apply(check(request)); 051 } 052 053 /** 054 * Allocates an array of type T of the requested size. 055 * 056 * @param <T> The return array type 057 * @param request The requested size. 058 * @param factory The array factory. 059 * @param eltShallowByteSize The shallow byte size of an element. 060 * @return a new byte array. 061 * @throws AllocationRequestException Thrown when the request exceeds the limit. 062 * @see #check(int) 063 */ 064 public static <T> T[] array(final int request, final IntFunction<T[]> factory, final int eltShallowByteSize) { 065 check(request * eltShallowByteSize); 066 return factory.apply(request); 067 } 068 069 /** 070 * Allocates an Object array of type T of the requested size. 071 * 072 * @param <T> The return array type 073 * @param request The requested size. 074 * @return a new byte array. 075 * @throws AllocationRequestException Thrown when the request exceeds the limit. 076 * @see #check(int) 077 */ 078 public static <T> ArrayList<T> arrayList(final int request) { 079 check(24 + request * 4); // 4 bytes per element 080 return apply(request, ArrayList::new); 081 } 082 083 /** 084 * Allocates a byte array of the requested size. 085 * 086 * @param request The requested size. 087 * @return a new byte array. 088 * @throws AllocationRequestException Thrown when the request exceeds the limit. 089 * @see #check(int, int) 090 */ 091 public static byte[] byteArray(final int request) { 092 return new byte[checkByteArray(request)]; 093 } 094 095 /** 096 * Allocates a byte array of the requested size. 097 * 098 * @param request The requested size is cast down to an int. 099 * @return a new byte array. 100 * @throws AllocationRequestException Thrown when the request exceeds the limit. 101 * @see #check(int, int) 102 */ 103 public static byte[] byteArray(final long request) { 104 return new byte[check(request, Byte.BYTES)]; 105 } 106 107 /** 108 * Allocates a char array of the requested size. 109 * 110 * @param request The requested size. 111 * @return a new char array. 112 * @throws AllocationRequestException Thrown when the request exceeds the limit. 113 * @see #check(int, int) 114 */ 115 public static char[] charArray(final int request) { 116 return new char[check(request, Character.BYTES)]; 117 } 118 119 /** 120 * Checks a request for meeting allocation limits. 121 * <p> 122 * The default limit is {@code #DEFAULT}, override with the system property "org.apache.commons.imaging.common.mylzw.AllocationChecker". 123 * </p> 124 * 125 * @param request an allocation request. 126 * @return the request. 127 * @throws AllocationRequestException Thrown when the request exceeds the limit. 128 */ 129 public static int check(final int request) { 130 if (request > LIMIT) { 131 throw new AllocationRequestException(LIMIT, request); 132 } 133 return request; 134 } 135 136 /** 137 * Checks a request for meeting allocation limits. 138 * <p> 139 * The default limit is {@code #DEFAULT}, override with the system property "org.apache.commons.imaging.common.mylzw.AllocationChecker". 140 * </p> 141 * 142 * @param request an allocation request count. 143 * @param elementSize The element size. 144 * @return the request. 145 * @throws AllocationRequestException Thrown when the request exceeds the limit. 146 */ 147 public static int check(final int request, final int elementSize) { 148 final int multiplyExact; 149 try { 150 multiplyExact = Math.multiplyExact(request, elementSize); 151 } catch (final ArithmeticException e) { 152 throw new AllocationRequestException(LIMIT, BigInteger.valueOf(request).multiply(BigInteger.valueOf(elementSize)), e); 153 } 154 if (multiplyExact > LIMIT) { 155 throw new AllocationRequestException(LIMIT, request); 156 } 157 return request; 158 } 159 160 /** 161 * Checks a request for meeting allocation limits. 162 * <p> 163 * The default limit is {@code #DEFAULT}, override with the system property "org.apache.commons.imaging.common.mylzw.AllocationChecker". 164 * </p> 165 * 166 * @param request an allocation request count is cast down to an int. 167 * @param elementSize The element size. 168 * @return the request. 169 * @throws AllocationRequestException Thrown when the request exceeds the limit. 170 */ 171 public static int check(final long request, final int elementSize) { 172 try { 173 return check(Math.toIntExact(request), elementSize); 174 } catch (final ArithmeticException e) { 175 throw new AllocationRequestException(LIMIT, request, e); 176 } 177 } 178 179 /** 180 * Checks that allocating a byte array of the requested size is within the limit. 181 * 182 * @param request The byte array size. 183 * @return The input request. 184 */ 185 public static int checkByteArray(final int request) { 186 return check(request, Byte.BYTES); 187 } 188 189 /** 190 * Allocates a double array of the requested size. 191 * 192 * @param request The requested size. 193 * @return a new double array. 194 * @throws AllocationRequestException Thrown when the request exceeds the limit. 195 * @see #check(int, int) 196 */ 197 public static double[] doubleArray(final int request) { 198 return new double[check(request, Double.BYTES)]; 199 } 200 201 /** 202 * Allocates a float array of the requested size. 203 * 204 * @param request The requested size. 205 * @return a new float array. 206 * @throws AllocationRequestException Thrown when the request exceeds the limit. 207 * @see #check(int, int) 208 */ 209 public static float[] floatArray(final int request) { 210 return new float[check(request, Float.BYTES)]; 211 } 212 213 /** 214 * Allocates a int array of the requested size. 215 * 216 * @param request The requested size. 217 * @return a new int array. 218 * @throws AllocationRequestException Thrown when the request exceeds the limit. 219 * @see #check(int, int) 220 */ 221 public static int[] intArray(final int request) { 222 return new int[check(request, Integer.BYTES)]; 223 } 224 225 /** 226 * Allocates a long array of the requested size. 227 * 228 * @param request The requested size. 229 * @return a new long array. 230 * @throws AllocationRequestException Thrown when the request exceeds the limit. 231 * @see #check(int, int) 232 */ 233 public static long[] longArray(final int request) { 234 return new long[check(request, Long.BYTES)]; 235 } 236 237 /** 238 * Allocates a short array of the requested size. 239 * 240 * @param request The requested size. 241 * @return a new short array. 242 * @throws AllocationRequestException Thrown when the request exceeds the limit. 243 * @see #check(int, int) 244 */ 245 public static short[] shortArray(final int request) { 246 return new short[check(request, Short.BYTES)]; 247 } 248 249}