1 /* 2 * Copyright (C) 2011, Google Inc. 3 * and other copyright owners as documented in the project's IP log. 4 * 5 * This program and the accompanying materials are made available 6 * under the terms of the Eclipse Distribution License v1.0 which 7 * accompanies this distribution, is reproduced below, and is 8 * available at http://www.eclipse.org/org/documents/edl-v10.php 9 * 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or 13 * without modification, are permitted provided that the following 14 * conditions are met: 15 * 16 * - Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 19 * - Redistributions in binary form must reproduce the above 20 * copyright notice, this list of conditions and the following 21 * disclaimer in the documentation and/or other materials provided 22 * with the distribution. 23 * 24 * - Neither the name of the Eclipse Foundation, Inc. nor the 25 * names of its contributors may be used to endorse or promote 26 * products derived from this software without specific prior 27 * written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 30 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 32 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 34 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 35 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 36 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 38 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 41 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 42 */ 43 44 package org.eclipse.jgit.lib; 45 46 import java.util.Arrays; 47 import java.util.Iterator; 48 import java.util.NoSuchElementException; 49 50 /** 51 * Fast, efficient map for {@link ObjectId} subclasses in only one map. 52 * <p> 53 * To use this map type, applications must have their entry value type extend 54 * from {@link ObjectIdOwnerMap.Entry}, which itself extends from ObjectId. 55 * <p> 56 * Object instances may only be stored in <b>ONE</b> ObjectIdOwnerMap. This 57 * restriction exists because the map stores internal map state within each 58 * object instance. If an instance is be placed in another ObjectIdOwnerMap it 59 * could corrupt one or both map's internal state. 60 * <p> 61 * If an object instance must be in more than one map, applications may use 62 * ObjectIdOwnerMap for one of the maps, and {@link ObjectIdSubclassMap} for the 63 * other map(s). It is encouraged to use ObjectIdOwnerMap for the map that is 64 * accessed most often, as this implementation runs faster than the more general 65 * ObjectIdSubclassMap implementation. 66 * 67 * @param <V> 68 * type of subclass of ObjectId that will be stored in the map. 69 */ 70 public class ObjectIdOwnerMap<V extends ObjectIdOwnerMap.Entry> 71 implements Iterable<V>, ObjectIdSet { 72 /** Size of the initial directory, will grow as necessary. */ 73 private static final int INITIAL_DIRECTORY = 1024; 74 75 /** Number of bits in a segment's index. Segments are 2^11 in size. */ 76 private static final int SEGMENT_BITS = 11; 77 78 private static final int SEGMENT_SHIFT = 32 - SEGMENT_BITS; 79 80 /** 81 * Top level directory of the segments. 82 * <p> 83 * The low {@link #bits} of the SHA-1 are used to select the segment from 84 * this directory. Each segment is constant sized at 2^SEGMENT_BITS. 85 */ 86 V[][] directory; 87 88 /** Total number of objects in this map. */ 89 int size; 90 91 /** The map doubles in capacity when {@link #size} reaches this target. */ 92 private int grow; 93 94 /** Number of low bits used to form the index into {@link #directory}. */ 95 int bits; 96 97 /** Low bit mask to index into {@link #directory}, {@code 2^bits-1}. */ 98 private int mask; 99 100 /** Create an empty map. */ 101 @SuppressWarnings("unchecked") 102 public ObjectIdOwnerMap() { 103 bits = 0; 104 mask = 0; 105 grow = computeGrowAt(bits); 106 107 directory = (V[][]) new Entry[INITIAL_DIRECTORY][]; 108 directory[0] = newSegment(); 109 } 110 111 /** Remove all entries from this map. */ 112 public void clear() { 113 size = 0; 114 115 for (V[] tbl : directory) { 116 if (tbl == null) 117 break; 118 Arrays.fill(tbl, null); 119 } 120 } 121 122 /** 123 * Lookup an existing mapping. 124 * 125 * @param toFind 126 * the object identifier to find. 127 * @return the instance mapped to toFind, or null if no mapping exists. 128 */ 129 @SuppressWarnings("unchecked") 130 public V get(final AnyObjectId toFind) { 131 int h = toFind.w1; 132 V obj = directory[h & mask][h >>> SEGMENT_SHIFT]; 133 for (; obj != null; obj = (V) obj.next) 134 if (equals(obj, toFind)) 135 return obj; 136 return null; 137 } 138 139 /** 140 * Returns true if this map contains the specified object. 141 * 142 * @param toFind 143 * object to find. 144 * @return true if the mapping exists for this object; false otherwise. 145 */ 146 public boolean contains(final AnyObjectId toFind) { 147 return get(toFind) != null; 148 } 149 150 /** 151 * Store an object for future lookup. 152 * <p> 153 * An existing mapping for <b>must not</b> be in this map. Callers must 154 * first call {@link #get(AnyObjectId)} to verify there is no current 155 * mapping prior to adding a new mapping, or use {@link #addIfAbsent(Entry)}. 156 * 157 * @param newValue 158 * the object to store. 159 * @param <Q> 160 * type of instance to store. 161 */ 162 public <Q extends V> void add(final Q newValue) { 163 if (++size == grow) 164 grow(); 165 166 int h = newValue.w1; 167 V[] table = directory[h & mask]; 168 h >>>= SEGMENT_SHIFT; 169 170 newValue.next = table[h]; 171 table[h] = newValue; 172 } 173 174 /** 175 * Store an object for future lookup. 176 * <p> 177 * Stores {@code newValue}, but only if there is not already an object for 178 * the same object name. Callers can tell if the value is new by checking 179 * the return value with reference equality: 180 * 181 * <pre> 182 * V obj = ...; 183 * boolean wasNew = map.addIfAbsent(obj) == obj; 184 * </pre> 185 * 186 * @param newValue 187 * the object to store. 188 * @return {@code newValue} if stored, or the prior value already stored and 189 * that would have been returned had the caller used 190 * {@code get(newValue)} first. 191 * @param <Q> 192 * type of instance to store. 193 */ 194 @SuppressWarnings("unchecked") 195 public <Q extends V> V addIfAbsent(final Q newValue) { 196 int h = newValue.w1; 197 V[] table = directory[h & mask]; 198 h >>>= SEGMENT_SHIFT; 199 200 for (V obj = table[h]; obj != null; obj = (V) obj.next) 201 if (equals(obj, newValue)) 202 return obj; 203 204 newValue.next = table[h]; 205 table[h] = newValue; 206 207 if (++size == grow) 208 grow(); 209 return newValue; 210 } 211 212 /** @return number of objects in this map. */ 213 public int size() { 214 return size; 215 } 216 217 /** @return true if {@link #size()} is 0. */ 218 public boolean isEmpty() { 219 return size == 0; 220 } 221 222 public Iterator<V> iterator() { 223 return new Iterator<V>() { 224 private int found; 225 226 private int dirIdx; 227 228 private int tblIdx; 229 230 private V next; 231 232 public boolean hasNext() { 233 return found < size; 234 } 235 236 public V next() { 237 if (next != null) 238 return found(next); 239 240 for (;;) { 241 V[] table = directory[dirIdx]; 242 if (tblIdx == table.length) { 243 if (++dirIdx >= (1 << bits)) 244 throw new NoSuchElementException(); 245 table = directory[dirIdx]; 246 tblIdx = 0; 247 } 248 249 while (tblIdx < table.length) { 250 V v = table[tblIdx++]; 251 if (v != null) 252 return found(v); 253 } 254 } 255 } 256 257 @SuppressWarnings("unchecked") 258 private V found(V v) { 259 found++; 260 next = (V) v.next; 261 return v; 262 } 263 264 public void remove() { 265 throw new UnsupportedOperationException(); 266 } 267 }; 268 } 269 270 @SuppressWarnings("unchecked") 271 private void grow() { 272 final int oldDirLen = 1 << bits; 273 final int s = 1 << bits; 274 275 bits++; 276 mask = (1 << bits) - 1; 277 grow = computeGrowAt(bits); 278 279 // Quadruple the directory if it needs to expand. Expanding the 280 // directory is expensive because it generates garbage, so try 281 // to avoid doing it often. 282 // 283 final int newDirLen = 1 << bits; 284 if (directory.length < newDirLen) { 285 V[][] newDir = (V[][]) new Entry[newDirLen << 1][]; 286 System.arraycopy(directory, 0, newDir, 0, oldDirLen); 287 directory = newDir; 288 } 289 290 // For every bucket of every old segment, split the chain between 291 // the old segment and the new segment's corresponding bucket. To 292 // select between them use the lowest bit that was just added into 293 // the mask above. This causes the table to double in capacity. 294 // 295 for (int dirIdx = 0; dirIdx < oldDirLen; dirIdx++) { 296 final V[] oldTable = directory[dirIdx]; 297 final V[] newTable = newSegment(); 298 299 for (int i = 0; i < oldTable.length; i++) { 300 V chain0 = null; 301 V chain1 = null; 302 V next; 303 304 for (V obj = oldTable[i]; obj != null; obj = next) { 305 next = (V) obj.next; 306 307 if ((obj.w1 & s) == 0) { 308 obj.next = chain0; 309 chain0 = obj; 310 } else { 311 obj.next = chain1; 312 chain1 = obj; 313 } 314 } 315 316 oldTable[i] = chain0; 317 newTable[i] = chain1; 318 } 319 320 directory[oldDirLen + dirIdx] = newTable; 321 } 322 } 323 324 @SuppressWarnings("unchecked") 325 private final V[] newSegment() { 326 return (V[]) new Entry[1 << SEGMENT_BITS]; 327 } 328 329 private static final int computeGrowAt(int bits) { 330 return 1 << (bits + SEGMENT_BITS); 331 } 332 333 private static final boolean equals(AnyObjectId firstObjectId, 334 AnyObjectId secondObjectId) { 335 return firstObjectId.w2 == secondObjectId.w2 336 && firstObjectId.w3 == secondObjectId.w3 337 && firstObjectId.w4 == secondObjectId.w4 338 && firstObjectId.w5 == secondObjectId.w5 339 && firstObjectId.w1 == secondObjectId.w1; 340 } 341 342 /** Type of entry stored in the {@link ObjectIdOwnerMap}. */ 343 public static abstract class Entry extends ObjectId { 344 Entry next; 345 346 /** 347 * Initialize this entry with a specific ObjectId. 348 * 349 * @param id 350 * the id the entry represents. 351 */ 352 public Entry(AnyObjectId id) { 353 super(id); 354 } 355 } 356 }