/* * Copyright (C) 2011, Google Inc. and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at * https://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.lib; import java.util.Arrays; import java.util.Iterator; import java.util.NoSuchElementException; /** * Fast, efficient map for {@link org.eclipse.jgit.lib.ObjectId} subclasses in * only one map. *
* To use this map type, applications must have their entry value type extend * from {@link org.eclipse.jgit.lib.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 org.eclipse.jgit.lib.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.
*/
V[][] directory;
/** Total number of objects in this map. */
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}. */
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(AnyObjectId toFind) {
if (toFind == null) {
return null;
}
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;
}
/**
* {@inheritDoc}
*
* Returns true if this map contains the specified object.
*/
@Override
public boolean contains(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.
*/
public
* 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:
*
* void add(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.
*/
@SuppressWarnings("unchecked")
public V addIfAbsent(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;
}
/**
* Get number of objects in this map.
*
* @return number of objects in this map.
*/
public int size() {
return size;
}
/**
* Whether this map is empty
*
* @return true if {@link #size()} is 0.
*/
public boolean isEmpty() {
return size == 0;
}
/** {@inheritDoc} */
@Override
public Iterator