ObjectIdSubclassMap.java

  1. /*
  2.  * Copyright (C) 2009, Google Inc.
  3.  * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
  4.  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
  5.  *
  6.  * This program and the accompanying materials are made available under the
  7.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  8.  * https://www.eclipse.org/org/documents/edl-v10.php.
  9.  *
  10.  * SPDX-License-Identifier: BSD-3-Clause
  11.  */

  12. package org.eclipse.jgit.lib;

  13. import java.util.Iterator;
  14. import java.util.NoSuchElementException;

  15. /**
  16.  * Fast, efficient map specifically for {@link org.eclipse.jgit.lib.ObjectId}
  17.  * subclasses.
  18.  * <p>
  19.  * This map provides an efficient translation from any ObjectId instance to a
  20.  * cached subclass of ObjectId that has the same value.
  21.  * <p>
  22.  * If object instances are stored in only one map,
  23.  * {@link org.eclipse.jgit.lib.ObjectIdOwnerMap} is a more efficient
  24.  * implementation.
  25.  *
  26.  * @param <V>
  27.  *            type of subclass of ObjectId that will be stored in the map.
  28.  */
  29. public class ObjectIdSubclassMap<V extends ObjectId>
  30.         implements Iterable<V>, ObjectIdSet {
  31.     private static final int INITIAL_TABLE_SIZE = 2048;

  32.     int size;

  33.     private int grow;

  34.     private int mask;

  35.     V[] table;

  36.     /**
  37.      * Create an empty map.
  38.      */
  39.     public ObjectIdSubclassMap() {
  40.         initTable(INITIAL_TABLE_SIZE);
  41.     }

  42.     /**
  43.      * Remove all entries from this map.
  44.      */
  45.     public void clear() {
  46.         size = 0;
  47.         initTable(INITIAL_TABLE_SIZE);
  48.     }

  49.     /**
  50.      * Lookup an existing mapping.
  51.      *
  52.      * @param toFind
  53.      *            the object identifier to find.
  54.      * @return the instance mapped to toFind, or null if no mapping exists.
  55.      */
  56.     public V get(AnyObjectId toFind) {
  57.         final int msk = mask;
  58.         int i = toFind.w1 & msk;
  59.         final V[] tbl = table;
  60.         V obj;

  61.         while ((obj = tbl[i]) != null) {
  62.             if (AnyObjectId.isEqual(obj, toFind)) {
  63.                 return obj;
  64.             }
  65.             i = (i + 1) & msk;
  66.         }
  67.         return null;
  68.     }

  69.     /**
  70.      * {@inheritDoc}
  71.      * <p>
  72.      * Returns true if this map contains the specified object.
  73.      */
  74.     @Override
  75.     public boolean contains(AnyObjectId toFind) {
  76.         return get(toFind) != null;
  77.     }

  78.     /**
  79.      * Store an object for future lookup.
  80.      * <p>
  81.      * An existing mapping for <b>must not</b> be in this map. Callers must
  82.      * first call {@link #get(AnyObjectId)} to verify there is no current
  83.      * mapping prior to adding a new mapping, or use
  84.      * {@link #addIfAbsent(ObjectId)}.
  85.      *
  86.      * @param newValue
  87.      *            the object to store.
  88.      */
  89.     public <Q extends V> void add(Q newValue) {
  90.         if (++size == grow)
  91.             grow();
  92.         insert(newValue);
  93.     }

  94.     /**
  95.      * Store an object for future lookup.
  96.      * <p>
  97.      * Stores {@code newValue}, but only if there is not already an object for
  98.      * the same object name. Callers can tell if the value is new by checking
  99.      * the return value with reference equality:
  100.      *
  101.      * <pre>
  102.      * V obj = ...;
  103.      * boolean wasNew = map.addIfAbsent(obj) == obj;
  104.      * </pre>
  105.      *
  106.      * @param newValue
  107.      *            the object to store.
  108.      * @return {@code newValue} if stored, or the prior value already stored and
  109.      *         that would have been returned had the caller used
  110.      *         {@code get(newValue)} first.
  111.      */
  112.     public <Q extends V> V addIfAbsent(Q newValue) {
  113.         final int msk = mask;
  114.         int i = newValue.w1 & msk;
  115.         final V[] tbl = table;
  116.         V obj;

  117.         while ((obj = tbl[i]) != null) {
  118.             if (AnyObjectId.isEqual(obj, newValue))
  119.                 return obj;
  120.             i = (i + 1) & msk;
  121.         }

  122.         if (++size == grow) {
  123.             grow();
  124.             insert(newValue);
  125.         } else {
  126.             tbl[i] = newValue;
  127.         }
  128.         return newValue;
  129.     }

  130.     /**
  131.      * Get number of objects in map
  132.      *
  133.      * @return number of objects in map
  134.      */
  135.     public int size() {
  136.         return size;
  137.     }

  138.     /**
  139.      * Whether {@link #size()} is 0.
  140.      *
  141.      * @return true if {@link #size()} is 0.
  142.      */
  143.     public boolean isEmpty() {
  144.         return size == 0;
  145.     }

  146.     /** {@inheritDoc} */
  147.     @Override
  148.     public Iterator<V> iterator() {
  149.         return new Iterator<>() {
  150.             private int found;

  151.             private int i;

  152.             @Override
  153.             public boolean hasNext() {
  154.                 return found < size;
  155.             }

  156.             @Override
  157.             public V next() {
  158.                 while (i < table.length) {
  159.                     final V v = table[i++];
  160.                     if (v != null) {
  161.                         found++;
  162.                         return v;
  163.                     }
  164.                 }
  165.                 throw new NoSuchElementException();
  166.             }

  167.             @Override
  168.             public void remove() {
  169.                 throw new UnsupportedOperationException();
  170.             }
  171.         };
  172.     }

  173.     private void insert(V newValue) {
  174.         final int msk = mask;
  175.         int j = newValue.w1 & msk;
  176.         final V[] tbl = table;
  177.         while (tbl[j] != null)
  178.             j = (j + 1) & msk;
  179.         tbl[j] = newValue;
  180.     }

  181.     private void grow() {
  182.         final V[] oldTable = table;
  183.         final int oldSize = table.length;

  184.         initTable(oldSize << 1);
  185.         for (int i = 0; i < oldSize; i++) {
  186.             final V obj = oldTable[i];
  187.             if (obj != null)
  188.                 insert(obj);
  189.         }
  190.     }

  191.     private void initTable(int sz) {
  192.         grow = sz >> 1;
  193.         mask = sz - 1;
  194.         table = createArray(sz);
  195.     }

  196.     @SuppressWarnings("unchecked")
  197.     private final V[] createArray(int sz) {
  198.         return (V[]) new ObjectId[sz];
  199.     }
  200. }