1 /* 2 * Copyright (c) 2013, 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 java.util; 25 26 /* 27 * @test 28 * @bug 8005698 29 * @summary Test the case where TreeBin.splitTreeBin() converts a bin back to an Entry list 30 * @library ../../../tools/launcher/ ../ 31 * @build TestHelper LaunchOnBootClassPath TreeBinSplitBackToEntries 32 * @run main LaunchOnBootClassPath java.util.TreeBinSplitBackToEntries unused 33 * @author Brent Christian 34 * 35 * this test requires overwriting hashSeed 36 * * or, ensuring that it is 0 37 * 38 * @author bchristi 39 */ 40 41 // TODO: does this test still work with TREE_THRESHOLD = 16? 42 43 public class TreeBinSplitBackToEntries { 44 private static int HASHMASK = 0x3F; 45 private static boolean verbose = false; 46 private static boolean fastFail = false; 47 private static boolean failed = false; 48 49 static sun.misc.Unsafe UNSAFE; 50 51 static { 52 try { 53 UNSAFE = sun.misc.Unsafe.getUnsafe(); 54 } catch (SecurityException e) { 55 e.printStackTrace(); 56 throw new Error("Problem setting up UNSAFE"); 57 } 58 } 59 60 private static HashMap clearHashSeed(HashMap map) { 61 try { 62 long HASHSEED_OFFSET = UNSAFE.objectFieldOffset(HashMap.class.getDeclaredField("hashSeed")); 63 //debug 64 UNSAFE.putIntVolatile(map, HASHSEED_OFFSET, 0); 65 return map; 66 } catch (NoSuchFieldException e) { 67 throw new Error("Problem setting up hashSeed for LinkedHashMap"); 68 } 69 } 70 71 private static WeakHashMap clearHashSeed(WeakHashMap map) { 72 try { 73 long HASHSEED_OFFSET = UNSAFE.objectFieldOffset(WeakHashMap.class.getDeclaredField("hashSeed")); 74 //debug 75 UNSAFE.putIntVolatile(map, HASHSEED_OFFSET, 0); 76 return map; 77 } catch (NoSuchFieldException e) { 78 throw new Error("Problem setting up hashSeed or NULL_KEY: " + e); 79 } 80 } 81 82 static void printlnIfVerbose(String msg) { 83 if (verbose) {System.out.println(msg); } 84 } 85 86 public static void main(String[] args) { 87 for (String arg : args) { 88 switch(arg) { 89 case "-verbose": 90 verbose = true; 91 break; 92 case "-fastfail": 93 fastFail = true; 94 break; 95 } 96 } 97 testMapHiTree(); 98 testMapLoTree(); 99 if (failed) { 100 System.out.println("Test Failed"); 101 System.exit(1); 102 } else { 103 System.out.println("Test Passed"); 104 } 105 } 106 107 public static void testMapHiTree() { 108 Object[][] mapKeys = makeHiTreeTestData(); 109 testMapsForKeys(mapKeys, "hiTree"); 110 } 111 112 public static void testMapLoTree() { 113 Object[][] mapKeys = makeLoTreeTestData(); 114 115 testMapsForKeys(mapKeys, "loTree"); 116 } 117 118 public static void testMapsForKeys(Object[][] mapKeys, String desc) { 119 // loop through data sets 120 for (Object[] keys_desc : mapKeys) { 121 Map<Object, Object>[] maps = (Map<Object, Object>[]) new Map[]{ 122 clearHashSeed(new HashMap<>(4, 0.8f)), 123 clearHashSeed(new LinkedHashMap<>(4, 0.8f)), 124 // new TreeMap<>(), 125 clearHashSeed(new WeakHashMap<>(4, 0.8f)), 126 // TreeSet? 127 // How would you poke into the TreeSet's HashMap's hashSeed? 128 }; 129 130 // for each map type. 131 for (Map<Object, Object> map : maps) { 132 Object[] keys = (Object[]) keys_desc[1]; 133 System.out.println(desc + ": testPutThenGet() for " + map.getClass()); 134 testPutThenGet(map, keys); 135 } 136 } 137 } 138 139 private static <T> void testPutThenGet(Map<T, T> map, T[] keys) { 140 for (T key : keys) { 141 printlnIfVerbose("put()ing 0x" + Integer.toHexString(Integer.parseInt(key.toString())) + ", hashCode=" + Integer.toHexString(key.hashCode())); 142 map.put(key, key); 143 } 144 for (T key : keys) { 145 check("key: 0x" + Integer.toHexString(Integer.parseInt(key.toString())) + " not found in resulting " + map.getClass().getSimpleName(), map.get(key) != null); 146 } 147 } 148 149 /* Data to force the loTree in TreeBin.splitTreeBin() to be converted back 150 * into an Entry list 151 */ 152 private static Object[][] makeLoTreeTestData() { 153 HashableInteger COLLIDING_OBJECTS[] = new HashableInteger[] { 154 155 new HashableInteger(0x01, HASHMASK), 156 new HashableInteger(0x101, HASHMASK), 157 new HashableInteger(0x10, HASHMASK), 158 new HashableInteger(0x20, HASHMASK), 159 new HashableInteger( 0x110, HASHMASK), 160 new HashableInteger( 0x310, HASHMASK), 161 new HashableInteger( 0x510, HASHMASK), 162 new HashableInteger( 0x710, HASHMASK), 163 new HashableInteger( 0x910, HASHMASK), 164 new HashableInteger( 0xB10, HASHMASK), 165 new HashableInteger( 0xF10, HASHMASK), 166 new HashableInteger(0x1130, HASHMASK), 167 new HashableInteger(0x2150, HASHMASK), 168 new HashableInteger(0x4170, HASHMASK), 169 new HashableInteger(0x8190, HASHMASK), 170 }; 171 return new Object[][] { 172 new Object[]{"Colliding Objects", COLLIDING_OBJECTS}, 173 }; 174 } 175 176 /* Data to force the hiTree in TreeBin.splitTreeBin() to be converted back 177 * into an Entry list 178 */ 179 private static Object[][] makeHiTreeTestData() { 180 HashableInteger COLLIDING_OBJECTS[] = new HashableInteger[] { 181 new HashableInteger( 0x01, HASHMASK), 182 new HashableInteger( 0x41, HASHMASK), 183 new HashableInteger( 0x101, HASHMASK), 184 new HashableInteger( 0x381, HASHMASK), 185 new HashableInteger(0x1101, HASHMASK), 186 new HashableInteger( 0x181, HASHMASK), 187 new HashableInteger( 0x310, HASHMASK), 188 new HashableInteger( 0x510, HASHMASK), 189 new HashableInteger( 0x710, HASHMASK), 190 new HashableInteger( 0x1C1, HASHMASK), 191 new HashableInteger( 0xC1, HASHMASK), 192 new HashableInteger( 0x81, HASHMASK), 193 new HashableInteger(0x2150, HASHMASK), 194 new HashableInteger(0x4170, HASHMASK), 195 new HashableInteger(0x8190, HASHMASK), 196 }; 197 return new Object[][] { 198 new Object[]{"Colliding Objects", COLLIDING_OBJECTS}, 199 }; 200 } 201 202 static void check(String desc, boolean cond) { 203 if (!cond) { 204 fail(desc); 205 } 206 } 207 208 static void fail(String msg) { 209 failed = true; 210 (new Error("Failure: " + msg)).printStackTrace(System.err); 211 if (fastFail) { 212 System.exit(1); 213 } 214 } 215 216 final static class HashableInteger implements Comparable<HashableInteger> { 217 final int value; 218 final int hashmask; //yes duplication 219 220 HashableInteger(int value, int hashmask) { 221 this.value = value; 222 this.hashmask = hashmask; 223 } 224 225 @Override 226 public boolean equals(Object obj) { 227 if (obj instanceof HashableInteger) { 228 HashableInteger other = (HashableInteger) obj; 229 return other.value == value; 230 } 231 return false; 232 } 233 234 @Override 235 public int hashCode() { 236 // This version ANDs the mask 237 return value & hashmask; 238 } 239 240 @Override 241 public int compareTo(HashableInteger o) { 242 return value - o.value; 243 } 244 245 @Override 246 public String toString() { 247 return Integer.toString(value); 248 } 249 } 250 }