1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package jdk.testlibrary; 25 26 import java.lang.reflect.Field; 27 import java.util.ArrayList; 28 import java.util.HashMap; 29 import java.util.IdentityHashMap; 30 31 /** 32 * Utility functions to check that the static storages are pre-sized 33 * optimally. 34 */ 35 public final class OptimalCapacity { 36 37 private OptimalCapacity() {} 38 39 /** 40 * Checks adequacy of the initial capacity of a static field 41 * of type {@code ArrayList}. 42 * 43 * Having 44 * <pre> 45 * class XClass { 46 * static ArrayList theList = new ArrayList(N); 47 * } 48 * </pre> 49 * 50 * you should call from the test 51 * 52 * <pre> 53 * OptimalCapacity.assertProperlySized(XClass.class, "theList", N); 54 * </pre> 55 */ 56 public static void ofArrayList(Class<?> clazz, String fieldName, 57 int initialCapacity) 58 { 59 try { 60 Field field = clazz.getDeclaredField(fieldName); 61 field.setAccessible(true); 62 Object obj = field.get(null); 63 if (!ArrayList.class.equals(obj.getClass())) { 64 throw new RuntimeException("'" + field + 65 "' expected to be of type ArrayList"); 66 } 67 ArrayList<?> list = (ArrayList<?>)obj; 68 69 // For ArrayList the optimal capacity is its final size 70 if (list.size() != initialCapacity) { 71 throw new RuntimeException("Size of '" + field + 72 "' is " + list.size() + 73 ", but expected to be " + initialCapacity); 74 } 75 if (internalArraySize(list) != initialCapacity) { 76 throw new RuntimeException("Capacity of '" + field + 77 "' is " + internalArraySize(list) + 78 ", but expected to be " + initialCapacity); 79 } 80 } catch (ReflectiveOperationException roe) { 81 throw new RuntimeException(roe); 82 } 83 } 84 85 /** 86 * Checks adequacy of the initial capacity of a static field 87 * of type {@code HashMap}. 88 * 89 * Having 90 * <pre> 91 * class XClass { 92 * static HashMap theMap = new HashMap(N); 93 * } 94 * </pre> 95 * 96 * you should call from the test 97 * 98 * <pre> 99 * OptimalCapacity.ofHashMap(XClass.class, "theMap", N); 100 * </pre> 101 */ 102 public static void ofHashMap(Class<?> clazz, String fieldName, 103 int initialCapacity) 104 { 105 try { 106 Field field = clazz.getDeclaredField(fieldName); 107 field.setAccessible(true); 108 Object obj = field.get(null); 109 if (!HashMap.class.equals(obj.getClass())) { 110 throw new RuntimeException(field + 111 " expected to be of type HashMap"); 112 } 113 HashMap<?,?> map = (HashMap<?,?>)obj; 114 115 // Check that the map allocates only necessary amount of space 116 HashMap<Object, Object> tmp = new HashMap<>(map); 117 if (internalArraySize(map) != internalArraySize(tmp)) { 118 throw new RuntimeException("Final capacity of '" + field + 119 "' is " + internalArraySize(map) + 120 ", which exceeds necessary minimum " + internalArraySize(tmp)); 121 } 122 123 // Check that map is initially properly sized 124 tmp = new HashMap<>(initialCapacity); 125 tmp.put(new Object(), new Object()); // trigger storage init 126 if (internalArraySize(map) != internalArraySize(tmp)) { 127 throw new RuntimeException("Requested capacity of '" + field + 128 "' was " + initialCapacity + 129 ", which resulted in final capacity " + internalArraySize(tmp) + 130 ", which differs from necessary minimum " + internalArraySize(map)); 131 } 132 133 } catch (ReflectiveOperationException roe) { 134 throw new RuntimeException(roe); 135 } 136 } 137 138 /** 139 * Checks adequacy of the expected maximum size of a static field 140 * of type {@code IdentityHashMap}. 141 * 142 * Having 143 * <pre> 144 * class XClass { 145 * static IdentityHashMap theMap = new IdentityHashMap(M); 146 * } 147 * </pre> 148 * 149 * you should call from the test 150 * 151 * <pre> 152 * OptimalCapacity.ofIdentityHashMap(XClass.class, "theMap", M); 153 * </pre> 154 */ 155 public static void ofIdentityHashMap(Class<?> clazz, String fieldName, 156 int expectedMaxSize) 157 { 158 try { 159 Field field = clazz.getDeclaredField(fieldName); 160 field.setAccessible(true); 161 Object obj = field.get(null); 162 if (!IdentityHashMap.class.equals(obj.getClass())) { 163 throw new RuntimeException("'" + field + 164 "' expected to be of type IdentityHashMap"); 165 } 166 IdentityHashMap<?,?> map = (IdentityHashMap<?,?>)obj; 167 168 // Check that size of map is what was expected 169 if (map.size() != expectedMaxSize) { 170 throw new RuntimeException("Size of '" + field + 171 "' is " + map.size() + 172 ", which differs from expected " + expectedMaxSize); 173 } 174 175 // Check that the map allocated only necessary amount of memory 176 IdentityHashMap<Object, Object> tmp = new IdentityHashMap<>(map); 177 if (internalArraySize(map) != internalArraySize(tmp)) { 178 throw new RuntimeException("Final capacity of '" + field + 179 "' is " + internalArraySize(map) + 180 ", which exceeds necessary minimum " + internalArraySize(tmp)); 181 } 182 183 // Check that map was initially properly sized 184 tmp = new IdentityHashMap<>(expectedMaxSize); 185 tmp.put(new Object(), new Object()); // trigger storage init 186 if (internalArraySize(map) != internalArraySize(tmp)) { 187 throw new RuntimeException("Requested number of elements in '" + field + 188 "' was " + expectedMaxSize + 189 ", which resulted in final capacity " + internalArraySize(tmp) + 190 ", which differs from necessary minimum " + internalArraySize(map)); 191 } 192 } catch (ReflectiveOperationException roe) { 193 throw new RuntimeException(roe); 194 } 195 } 196 197 /** 198 * Returns size of the internal storage. 199 */ 200 private static int internalArraySize(Object container) 201 throws ReflectiveOperationException { 202 Field field; 203 if (ArrayList.class.equals(container.getClass())) { 204 field = ArrayList.class.getDeclaredField("elementData"); 205 } else if (HashMap.class.equals(container.getClass())) { 206 field = HashMap.class.getDeclaredField("table"); 207 } else if (IdentityHashMap.class.equals(container.getClass())) { 208 field = IdentityHashMap.class.getDeclaredField("table"); 209 } else { 210 throw new RuntimeException("Unexpected class " + 211 container.getClass()); 212 } 213 field.setAccessible(true); 214 return ((Object[])field.get(container)).length; 215 } 216 }