/* * Copyright (C) 2011, Google Inc. * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available * under the terms of the Eclipse Distribution License v1.0 which * accompanies this distribution, is reproduced below, and is * available at http://www.eclipse.org/org/documents/edl-v10.php * * All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * - Neither the name of the Eclipse Foundation, Inc. nor the * names of its contributors may be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.eclipse.jgit.lib; import java.util.Arrays; import java.util.Iterator; import java.util.NoSuchElementException; /** * Fast, efficient map for {@link ObjectId} subclasses in only one map. *
* To use this map type, applications must have their entry value type extend * from {@link ObjectIdOwnerMap.Entry}, which itself extends from ObjectId. *
* Object instances may only be stored in ONE ObjectIdOwnerMap. This * restriction exists because the map stores internal map state within each * object instance. If an instance is be placed in another ObjectIdOwnerMap it * could corrupt one or both map's internal state. *
* If an object instance must be in more than one map, applications may use
* ObjectIdOwnerMap for one of the maps, and {@link ObjectIdSubclassMap} for the
* other map(s). It is encouraged to use ObjectIdOwnerMap for the map that is
* accessed most often, as this implementation runs faster than the more general
* ObjectIdSubclassMap implementation.
*
* @param
* The low {@link #bits} of the SHA-1 are used to select the segment from
* this directory. Each segment is constant sized at 2^SEGMENT_BITS.
*/
private V[][] directory;
/** Total number of objects in this map. */
private int size;
/** The map doubles in capacity when {@link #size} reaches this target. */
private int grow;
/** Number of low bits used to form the index into {@link #directory}. */
private int bits;
/** Low bit mask to index into {@link #directory}, {@code 2^bits-1}. */
private int mask;
/** Create an empty map. */
@SuppressWarnings("unchecked")
public ObjectIdOwnerMap() {
bits = 0;
mask = 0;
grow = computeGrowAt(bits);
directory = (V[][]) new Entry[INITIAL_DIRECTORY][];
directory[0] = newSegment();
}
/** Remove all entries from this map. */
public void clear() {
size = 0;
for (V[] tbl : directory) {
if (tbl == null)
break;
Arrays.fill(tbl, null);
}
}
/**
* Lookup an existing mapping.
*
* @param toFind
* the object identifier to find.
* @return the instance mapped to toFind, or null if no mapping exists.
*/
@SuppressWarnings("unchecked")
public V get(final AnyObjectId toFind) {
int h = toFind.w1;
V obj = directory[h & mask][h >>> SEGMENT_SHIFT];
for (; obj != null; obj = (V) obj.next)
if (equals(obj, toFind))
return obj;
return null;
}
/**
* Returns true if this map contains the specified object.
*
* @param toFind
* object to find.
* @return true if the mapping exists for this object; false otherwise.
*/
public boolean contains(final AnyObjectId toFind) {
return get(toFind) != null;
}
/**
* Store an object for future lookup.
*
* An existing mapping for must not be in this map. Callers must
* first call {@link #get(AnyObjectId)} to verify there is no current
* mapping prior to adding a new mapping, or use {@link #addIfAbsent(Entry)}.
*
* @param newValue
* the object to store.
* @param
* Stores {@code newValue}, but only if there is not already an object for
* the same object name. Callers can tell if the value is new by checking
* the return value with reference equality:
*
*
* type of instance to store.
*/
public
void add(final Q newValue) {
if (++size == grow)
grow();
int h = newValue.w1;
V[] table = directory[h & mask];
h >>>= SEGMENT_SHIFT;
newValue.next = table[h];
table[h] = newValue;
}
/**
* Store an object for future lookup.
*
* V obj = ...;
* boolean wasNew = map.addIfAbsent(obj) == obj;
*
*
* @param newValue
* the object to store.
* @return {@code newValue} if stored, or the prior value already stored and
* that would have been returned had the caller used
* {@code get(newValue)} first.
* @param
* type of instance to store.
*/
@SuppressWarnings("unchecked")
public
V addIfAbsent(final Q newValue) {
int h = newValue.w1;
V[] table = directory[h & mask];
h >>>= SEGMENT_SHIFT;
for (V obj = table[h]; obj != null; obj = (V) obj.next)
if (equals(obj, newValue))
return obj;
newValue.next = table[h];
table[h] = newValue;
if (++size == grow)
grow();
return newValue;
}
/** @return number of objects in this map. */
public int size() {
return size;
}
/** @return true if {@link #size()} is 0. */
public boolean isEmpty() {
return size == 0;
}
public Iterator