View Javadoc
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  
13  package org.eclipse.jgit.lib;
14  
15  import java.util.Iterator;
16  import java.util.NoSuchElementException;
17  
18  /**
19   * Fast, efficient map specifically for {@link org.eclipse.jgit.lib.ObjectId}
20   * subclasses.
21   * <p>
22   * This map provides an efficient translation from any ObjectId instance to a
23   * cached subclass of ObjectId that has the same value.
24   * <p>
25   * If object instances are stored in only one map,
26   * {@link org.eclipse.jgit.lib.ObjectIdOwnerMap} is a more efficient
27   * implementation.
28   *
29   * @param <V>
30   *            type of subclass of ObjectId that will be stored in the map.
31   */
32  public class ObjectIdSubclassMap<V extends ObjectId>
33  		implements Iterable<V>, ObjectIdSet {
34  	private static final int INITIAL_TABLE_SIZE = 2048;
35  
36  	int size;
37  
38  	private int grow;
39  
40  	private int mask;
41  
42  	V[] table;
43  
44  	/**
45  	 * Create an empty map.
46  	 */
47  	public ObjectIdSubclassMap() {
48  		initTable(INITIAL_TABLE_SIZE);
49  	}
50  
51  	/**
52  	 * Remove all entries from this map.
53  	 */
54  	public void clear() {
55  		size = 0;
56  		initTable(INITIAL_TABLE_SIZE);
57  	}
58  
59  	/**
60  	 * Lookup an existing mapping.
61  	 *
62  	 * @param toFind
63  	 *            the object identifier to find.
64  	 * @return the instance mapped to toFind, or null if no mapping exists.
65  	 */
66  	public V get(AnyObjectId toFind) {
67  		final int msk = mask;
68  		int i = toFind.w1 & msk;
69  		final V[] tbl = table;
70  		V obj;
71  
72  		while ((obj = tbl[i]) != null) {
73  			if (AnyObjectId.isEqual(obj, toFind)) {
74  				return obj;
75  			}
76  			i = (i + 1) & msk;
77  		}
78  		return null;
79  	}
80  
81  	/**
82  	 * {@inheritDoc}
83  	 * <p>
84  	 * Returns true if this map contains the specified object.
85  	 */
86  	@Override
87  	public boolean contains(AnyObjectId toFind) {
88  		return get(toFind) != null;
89  	}
90  
91  	/**
92  	 * Store an object for future lookup.
93  	 * <p>
94  	 * An existing mapping for <b>must not</b> be in this map. Callers must
95  	 * first call {@link #get(AnyObjectId)} to verify there is no current
96  	 * mapping prior to adding a new mapping, or use
97  	 * {@link #addIfAbsent(ObjectId)}.
98  	 *
99  	 * @param newValue
100 	 *            the object to store.
101 	 */
102 	public <Q extends V> void add(Q newValue) {
103 		if (++size == grow)
104 			grow();
105 		insert(newValue);
106 	}
107 
108 	/**
109 	 * Store an object for future lookup.
110 	 * <p>
111 	 * Stores {@code newValue}, but only if there is not already an object for
112 	 * the same object name. Callers can tell if the value is new by checking
113 	 * the return value with reference equality:
114 	 *
115 	 * <pre>
116 	 * V obj = ...;
117 	 * boolean wasNew = map.addIfAbsent(obj) == obj;
118 	 * </pre>
119 	 *
120 	 * @param newValue
121 	 *            the object to store.
122 	 * @return {@code newValue} if stored, or the prior value already stored and
123 	 *         that would have been returned had the caller used
124 	 *         {@code get(newValue)} first.
125 	 */
126 	public <Q extends V> V addIfAbsent(Q newValue) {
127 		final int msk = mask;
128 		int i = newValue.w1 & msk;
129 		final V[] tbl = table;
130 		V obj;
131 
132 		while ((obj = tbl[i]) != null) {
133 			if (AnyObjectId.isEqual(obj, newValue))
134 				return obj;
135 			i = (i + 1) & msk;
136 		}
137 
138 		if (++size == grow) {
139 			grow();
140 			insert(newValue);
141 		} else {
142 			tbl[i] = newValue;
143 		}
144 		return newValue;
145 	}
146 
147 	/**
148 	 * Get number of objects in map
149 	 *
150 	 * @return number of objects in map
151 	 */
152 	public int size() {
153 		return size;
154 	}
155 
156 	/**
157 	 * Whether {@link #size()} is 0.
158 	 *
159 	 * @return true if {@link #size()} is 0.
160 	 */
161 	public boolean isEmpty() {
162 		return size == 0;
163 	}
164 
165 	/** {@inheritDoc} */
166 	@Override
167 	public Iterator<V> iterator() {
168 		return new Iterator<>() {
169 			private int found;
170 
171 			private int i;
172 
173 			@Override
174 			public boolean hasNext() {
175 				return found < size;
176 			}
177 
178 			@Override
179 			public V next() {
180 				while (i < table.length) {
181 					final V v = table[i++];
182 					if (v != null) {
183 						found++;
184 						return v;
185 					}
186 				}
187 				throw new NoSuchElementException();
188 			}
189 
190 			@Override
191 			public void remove() {
192 				throw new UnsupportedOperationException();
193 			}
194 		};
195 	}
196 
197 	private void insert(V newValue) {
198 		final int msk = mask;
199 		int j = newValue.w1 & msk;
200 		final V[] tbl = table;
201 		while (tbl[j] != null)
202 			j = (j + 1) & msk;
203 		tbl[j] = newValue;
204 	}
205 
206 	private void grow() {
207 		final V[] oldTable = table;
208 		final int oldSize = table.length;
209 
210 		initTable(oldSize << 1);
211 		for (int i = 0; i < oldSize; i++) {
212 			final V obj = oldTable[i];
213 			if (obj != null)
214 				insert(obj);
215 		}
216 	}
217 
218 	private void initTable(int sz) {
219 		grow = sz >> 1;
220 		mask = sz - 1;
221 		table = createArray(sz);
222 	}
223 
224 	@SuppressWarnings("unchecked")
225 	private final V[] createArray(int sz) {
226 		return (V[]) new ObjectId[sz];
227 	}
228 }