001 /* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013 package org.mmtk.utility; 014 015 import org.mmtk.policy.RawPageSpace; 016 import org.mmtk.vm.VM; 017 018 import org.vmmagic.pragma.*; 019 import org.vmmagic.unboxed.*; 020 021 /** 022 * This class implements a simple hashtable. It is intended for use 023 * in sanity checking or debugging, not high-performance algorithms.<p> 024 * 025 * This class is <i>not thread safe</i>. 026 */ 027 @Uninterruptible public abstract class SimpleHashtable implements Constants { 028 /** The number of low order bits to ignore */ 029 private static final int HASH_SHIFT = 3; 030 031 /** Offset to the key */ 032 private static final Offset KEY_OFFSET = Offset.zero(); 033 034 /** Offset to the data */ 035 private static final Offset DATA_OFFSET = Offset.fromIntSignExtend(BYTES_IN_WORD); 036 037 /** The size of each entry in the table */ 038 private final Extent entrySize; 039 040 /** The mask to use to get the hash code */ 041 private final Word mask; 042 043 /** The start address of the data table */ 044 private Address base; 045 046 /** The full size of the table */ 047 private final Extent size; 048 049 /** The space to use for allocating the data structure */ 050 private final RawPageSpace space; 051 052 /** Is this table valid (created) */ 053 private boolean valid; 054 055 /** 056 * Create a new data table of a specified size. 057 * 058 * @param rps The space to acquire the data structure from. 059 * @param logSize The log of the number of table entries. 060 * @param es The size of each entry. 061 */ 062 protected SimpleHashtable(RawPageSpace rps, int logSize, Extent es) { 063 mask = Word.fromIntZeroExtend((1 << logSize) - 1); 064 entrySize = es.plus(BYTES_IN_WORD); 065 size = Extent.fromIntZeroExtend((1 << logSize) * entrySize.toInt()); 066 base = Address.zero(); 067 space = rps; 068 valid = false; 069 } 070 071 /** 072 * Create a (zeroed) table. 073 */ 074 public final void acquireTable() { 075 base = space.acquire(Conversions.bytesToPages(size)); 076 VM.memory.zero(false, base, size); 077 valid = true; 078 } 079 080 /** 081 * Drop the table (after collection). 082 */ 083 public final void releaseTable() { 084 space.release(base); 085 valid = false; 086 } 087 088 /** 089 * @return True if this table has backing data and is ready for use. 090 */ 091 public final boolean isValid() { 092 return valid; 093 } 094 095 /** 096 * Retrieve a pointer to the entry for the given object, or zero if one 097 * does not exist, unless create is passed.<p> 098 * 099 * If create is {@code true}, the return is guaranteed to be non-{@code null}. 100 * 101 * @param key The key used to lookup. 102 * @param create Create a new entry if not found. 103 * @return A pointer to the reference or {@code null}. 104 */ 105 @Inline 106 public final Address getEntry(Word key, boolean create) { 107 int startIndex = computeHash(key); 108 int index = startIndex; 109 Word curAddress; 110 Address entry; 111 do { 112 entry = getEntry(index); 113 curAddress = entry.loadWord(KEY_OFFSET); 114 index = (index + 1) & mask.toInt(); 115 } while(curAddress.NE(key) && 116 !curAddress.isZero() && 117 index != startIndex); 118 119 if (index == startIndex) { 120 VM.assertions.fail("No room left in table!"); 121 } 122 123 if (curAddress.isZero()) { 124 if (!create) return Address.zero(); 125 entry.store(key, KEY_OFFSET); 126 } 127 128 return entry; 129 } 130 131 /** 132 * Compute the hashtable index for a given object. 133 * 134 * @param key The key. 135 * @return The index. 136 */ 137 @Inline 138 private int computeHash(Word key) { 139 return key.rshl(HASH_SHIFT).and(mask).toInt(); 140 } 141 142 /** 143 * Return the address of a specified entry in the table. 144 * 145 * @param index The index of the entry. 146 * @return An address to the entry. 147 */ 148 @Inline 149 private Address getEntry(int index) { 150 return base.plus(Extent.fromIntZeroExtend(index * entrySize.toInt())); 151 } 152 153 /** 154 * Does the passed object have an entry in the table? 155 * 156 * @param key The key to find an entry for 157 * @return True if there is an entry for that object. 158 */ 159 public final boolean contains(Word key) { 160 return !getEntry(key, false).isZero(); 161 } 162 163 /** 164 * @return The first non-zero element in the table, or null if 165 * the table is empty. 166 */ 167 public final Address getFirst() { 168 return getNext(base.minus(entrySize)); 169 } 170 171 /** 172 * The next element in the table after the passed entry, or 173 * null if it is the last entry. 174 * 175 * @param curr The object to look for the next entry from. 176 * @return The next entry or {@code null}. 177 */ 178 public final Address getNext(Address curr) { 179 Address entry = curr.plus(entrySize); 180 while (entry.LT(base.plus(size))) { 181 if (!entry.loadWord().isZero()) return entry; 182 entry = entry.plus(entrySize); 183 } 184 return Address.zero(); 185 } 186 187 /** 188 * Given an address of an entry, return a pointer to the payload. 189 * 190 * @param entry The entry 191 * @return The object reference. 192 */ 193 public static Address getPayloadAddress(Address entry) { 194 return entry.plus(DATA_OFFSET); 195 } 196 197 /** 198 * Given a key, return a pointer to the payload. 199 * 200 * @param key The key 201 * @return The object reference. 202 */ 203 public final Address getPayloadAddress(Word key) { 204 Address entry = getEntry(key, false); 205 if (entry.isZero()) return Address.zero(); 206 207 return entry.plus(DATA_OFFSET); 208 } 209 210 211 /** 212 * Return the key for a given entry. 213 * 214 * @param entry The entry. 215 * @return The key. 216 */ 217 public static Word getKey(Address entry) { 218 return entry.loadWord(KEY_OFFSET); 219 } 220 221 /** 222 * Update the key for a given entry. This operation is not 223 * safe without rehashing 224 * 225 * @param entry The entry to update. 226 * @param key The new key. 227 */ 228 public static void replaceKey(Address entry, Word key) { 229 entry.store(key, KEY_OFFSET); 230 } 231 232 }