View Javadoc
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 org.eclipse.jgit.lib.ObjectId} subclasses in
52   * only one map.
53   * <p>
54   * To use this map type, applications must have their entry value type extend
55   * from {@link org.eclipse.jgit.lib.ObjectIdOwnerMap.Entry}, which itself
56   * extends from ObjectId.
57   * <p>
58   * Object instances may only be stored in <b>ONE</b> ObjectIdOwnerMap. This
59   * restriction exists because the map stores internal map state within each
60   * object instance. If an instance is be placed in another ObjectIdOwnerMap it
61   * could corrupt one or both map's internal state.
62   * <p>
63   * If an object instance must be in more than one map, applications may use
64   * ObjectIdOwnerMap for one of the maps, and
65   * {@link org.eclipse.jgit.lib.ObjectIdSubclassMap} for the other map(s). It is
66   * encouraged to use ObjectIdOwnerMap for the map that is accessed most often,
67   * as this implementation runs faster than the more general ObjectIdSubclassMap
68   * implementation.
69   *
70   * @param <V>
71   *            type of subclass of ObjectId that will be stored in the map.
72   */
73  public class ObjectIdOwnerMap<V extends ObjectIdOwnerMap.Entry>
74  		implements Iterable<V>, ObjectIdSet {
75  	/** Size of the initial directory, will grow as necessary. */
76  	private static final int INITIAL_DIRECTORY = 1024;
77  
78  	/** Number of bits in a segment's index. Segments are 2^11 in size. */
79  	private static final int SEGMENT_BITS = 11;
80  
81  	private static final int SEGMENT_SHIFT = 32 - SEGMENT_BITS;
82  
83  	/**
84  	 * Top level directory of the segments.
85  	 * <p>
86  	 * The low {@link #bits} of the SHA-1 are used to select the segment from
87  	 * this directory. Each segment is constant sized at 2^SEGMENT_BITS.
88  	 */
89  	V[][] directory;
90  
91  	/** Total number of objects in this map. */
92  	int size;
93  
94  	/** The map doubles in capacity when {@link #size} reaches this target. */
95  	private int grow;
96  
97  	/** Number of low bits used to form the index into {@link #directory}. */
98  	int bits;
99  
100 	/** Low bit mask to index into {@link #directory}, {@code 2^bits-1}. */
101 	private int mask;
102 
103 	/**
104 	 * Create an empty map.
105 	 */
106 	@SuppressWarnings("unchecked")
107 	public ObjectIdOwnerMap() {
108 		bits = 0;
109 		mask = 0;
110 		grow = computeGrowAt(bits);
111 
112 		directory = (V[][]) new Entry[INITIAL_DIRECTORY][];
113 		directory[0] = newSegment();
114 	}
115 
116 	/**
117 	 * Remove all entries from this map.
118 	 */
119 	public void clear() {
120 		size = 0;
121 
122 		for (V[] tbl : directory) {
123 			if (tbl == null)
124 				break;
125 			Arrays.fill(tbl, null);
126 		}
127 	}
128 
129 	/**
130 	 * Lookup an existing mapping.
131 	 *
132 	 * @param toFind
133 	 *            the object identifier to find.
134 	 * @return the instance mapped to toFind, or null if no mapping exists.
135 	 */
136 	@SuppressWarnings("unchecked")
137 	public V get(AnyObjectId toFind) {
138 		if (toFind == null) {
139 			return null;
140 		}
141 		int h = toFind.w1;
142 		V obj = directory[h & mask][h >>> SEGMENT_SHIFT];
143 		for (; obj != null; obj = (V) obj.next)
144 			if (equals(obj, toFind))
145 				return obj;
146 		return null;
147 	}
148 
149 	/**
150 	 * {@inheritDoc}
151 	 * <p>
152 	 * Returns true if this map contains the specified object.
153 	 */
154 	@Override
155 	public boolean contains(AnyObjectId toFind) {
156 		return get(toFind) != null;
157 	}
158 
159 	/**
160 	 * Store an object for future lookup.
161 	 * <p>
162 	 * An existing mapping for <b>must not</b> be in this map. Callers must
163 	 * first call {@link #get(AnyObjectId)} to verify there is no current
164 	 * mapping prior to adding a new mapping, or use {@link #addIfAbsent(Entry)}.
165 	 *
166 	 * @param newValue
167 	 *            the object to store.
168 	 */
169 	public <Q extends V> void add(Q newValue) {
170 		if (++size == grow)
171 			grow();
172 
173 		int h = newValue.w1;
174 		V[] table = directory[h & mask];
175 		h >>>= SEGMENT_SHIFT;
176 
177 		newValue.next = table[h];
178 		table[h] = newValue;
179 	}
180 
181 	/**
182 	 * Store an object for future lookup.
183 	 * <p>
184 	 * Stores {@code newValue}, but only if there is not already an object for
185 	 * the same object name. Callers can tell if the value is new by checking
186 	 * the return value with reference equality:
187 	 *
188 	 * <pre>
189 	 * V obj = ...;
190 	 * boolean wasNew = map.addIfAbsent(obj) == obj;
191 	 * </pre>
192 	 *
193 	 * @param newValue
194 	 *            the object to store.
195 	 * @return {@code newValue} if stored, or the prior value already stored and
196 	 *         that would have been returned had the caller used
197 	 *         {@code get(newValue)} first.
198 	 */
199 	@SuppressWarnings("unchecked")
200 	public <Q extends V> V addIfAbsent(Q newValue) {
201 		int h = newValue.w1;
202 		V[] table = directory[h & mask];
203 		h >>>= SEGMENT_SHIFT;
204 
205 		for (V obj = table[h]; obj != null; obj = (V) obj.next)
206 			if (equals(obj, newValue))
207 				return obj;
208 
209 		newValue.next = table[h];
210 		table[h] = newValue;
211 
212 		if (++size == grow)
213 			grow();
214 		return newValue;
215 	}
216 
217 	/**
218 	 * Get number of objects in this map.
219 	 *
220 	 * @return number of objects in this map.
221 	 */
222 	public int size() {
223 		return size;
224 	}
225 
226 	/**
227 	 * Whether this map is empty
228 	 *
229 	 * @return true if {@link #size()} is 0.
230 	 */
231 	public boolean isEmpty() {
232 		return size == 0;
233 	}
234 
235 	/** {@inheritDoc} */
236 	@Override
237 	public Iterator<V> iterator() {
238 		return new Iterator<V>() {
239 			private int found;
240 			private int dirIdx;
241 			private int tblIdx;
242 			private V next;
243 
244 			@Override
245 			public boolean hasNext() {
246 				return found < size;
247 			}
248 
249 			@Override
250 			public V next() {
251 				if (next != null)
252 					return found(next);
253 
254 				for (;;) {
255 					V[] table = directory[dirIdx];
256 					if (tblIdx == table.length) {
257 						if (++dirIdx >= (1 << bits))
258 							throw new NoSuchElementException();
259 						table = directory[dirIdx];
260 						tblIdx = 0;
261 					}
262 
263 					while (tblIdx < table.length) {
264 						V v = table[tblIdx++];
265 						if (v != null)
266 							return found(v);
267 					}
268 				}
269 			}
270 
271 			@SuppressWarnings("unchecked")
272 			private V found(V v) {
273 				found++;
274 				next = (V) v.next;
275 				return v;
276 			}
277 
278 			@Override
279 			public void remove() {
280 				throw new UnsupportedOperationException();
281 			}
282 		};
283 	}
284 
285 	@SuppressWarnings("unchecked")
286 	private void grow() {
287 		final int oldDirLen = 1 << bits;
288 		final int s = 1 << bits;
289 
290 		bits++;
291 		mask = (1 << bits) - 1;
292 		grow = computeGrowAt(bits);
293 
294 		// Quadruple the directory if it needs to expand. Expanding the
295 		// directory is expensive because it generates garbage, so try
296 		// to avoid doing it often.
297 		//
298 		final int newDirLen = 1 << bits;
299 		if (directory.length < newDirLen) {
300 			V[][] newDir = (V[][]) new Entry[newDirLen << 1][];
301 			System.arraycopy(directory, 0, newDir, 0, oldDirLen);
302 			directory = newDir;
303 		}
304 
305 		// For every bucket of every old segment, split the chain between
306 		// the old segment and the new segment's corresponding bucket. To
307 		// select between them use the lowest bit that was just added into
308 		// the mask above. This causes the table to double in capacity.
309 		//
310 		for (int dirIdx = 0; dirIdx < oldDirLen; dirIdx++) {
311 			final V[] oldTable = directory[dirIdx];
312 			final V[] newTable = newSegment();
313 
314 			for (int i = 0; i < oldTable.length; i++) {
315 				V chain0 = null;
316 				V chain1 = null;
317 				V next;
318 
319 				for (V obj = oldTable[i]; obj != null; obj = next) {
320 					next = (V) obj.next;
321 
322 					if ((obj.w1 & s) == 0) {
323 						obj.next = chain0;
324 						chain0 = obj;
325 					} else {
326 						obj.next = chain1;
327 						chain1 = obj;
328 					}
329 				}
330 
331 				oldTable[i] = chain0;
332 				newTable[i] = chain1;
333 			}
334 
335 			directory[oldDirLen + dirIdx] = newTable;
336 		}
337 	}
338 
339 	@SuppressWarnings("unchecked")
340 	private final V[] newSegment() {
341 		return (V[]) new Entry[1 << SEGMENT_BITS];
342 	}
343 
344 	private static final int computeGrowAt(int bits) {
345 		return 1 << (bits + SEGMENT_BITS);
346 	}
347 
348 	private static final boolean equals(AnyObjectId firstObjectId,
349 			AnyObjectId secondObjectId) {
350 		return firstObjectId.w2 == secondObjectId.w2
351 				&& firstObjectId.w3 == secondObjectId.w3
352 				&& firstObjectId.w4 == secondObjectId.w4
353 				&& firstObjectId.w5 == secondObjectId.w5
354 				&& firstObjectId.w1 == secondObjectId.w1;
355 	}
356 
357 	/** Type of entry stored in the {@link ObjectIdOwnerMap}. */
358 	public static abstract class Entry extends ObjectId {
359 		transient Entry next;
360 
361 		/**
362 		 * Initialize this entry with a specific ObjectId.
363 		 *
364 		 * @param id
365 		 *            the id the entry represents.
366 		 */
367 		public Entry(AnyObjectId id) {
368 			super(id);
369 		}
370 	}
371 }