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 		int h = toFind.w1;
139 		V obj = directory[h & mask][h >>> SEGMENT_SHIFT];
140 		for (; obj != null; obj = (V) obj.next)
141 			if (equals(obj, toFind))
142 				return obj;
143 		return null;
144 	}
145 
146 	/**
147 	 * {@inheritDoc}
148 	 * <p>
149 	 * Returns true if this map contains the specified object.
150 	 */
151 	@Override
152 	public boolean contains(AnyObjectId toFind) {
153 		return get(toFind) != null;
154 	}
155 
156 	/**
157 	 * Store an object for future lookup.
158 	 * <p>
159 	 * An existing mapping for <b>must not</b> be in this map. Callers must
160 	 * first call {@link #get(AnyObjectId)} to verify there is no current
161 	 * mapping prior to adding a new mapping, or use {@link #addIfAbsent(Entry)}.
162 	 *
163 	 * @param newValue
164 	 *            the object to store.
165 	 */
166 	public <Q extends V> void add(Q newValue) {
167 		if (++size == grow)
168 			grow();
169 
170 		int h = newValue.w1;
171 		V[] table = directory[h & mask];
172 		h >>>= SEGMENT_SHIFT;
173 
174 		newValue.next = table[h];
175 		table[h] = newValue;
176 	}
177 
178 	/**
179 	 * Store an object for future lookup.
180 	 * <p>
181 	 * Stores {@code newValue}, but only if there is not already an object for
182 	 * the same object name. Callers can tell if the value is new by checking
183 	 * the return value with reference equality:
184 	 *
185 	 * <pre>
186 	 * V obj = ...;
187 	 * boolean wasNew = map.addIfAbsent(obj) == obj;
188 	 * </pre>
189 	 *
190 	 * @param newValue
191 	 *            the object to store.
192 	 * @return {@code newValue} if stored, or the prior value already stored and
193 	 *         that would have been returned had the caller used
194 	 *         {@code get(newValue)} first.
195 	 */
196 	@SuppressWarnings("unchecked")
197 	public <Q extends V> V addIfAbsent(Q newValue) {
198 		int h = newValue.w1;
199 		V[] table = directory[h & mask];
200 		h >>>= SEGMENT_SHIFT;
201 
202 		for (V obj = table[h]; obj != null; obj = (V) obj.next)
203 			if (equals(obj, newValue))
204 				return obj;
205 
206 		newValue.next = table[h];
207 		table[h] = newValue;
208 
209 		if (++size == grow)
210 			grow();
211 		return newValue;
212 	}
213 
214 	/**
215 	 * Get number of objects in this map.
216 	 *
217 	 * @return number of objects in this map.
218 	 */
219 	public int size() {
220 		return size;
221 	}
222 
223 	/**
224 	 * Whether this map is empty
225 	 *
226 	 * @return true if {@link #size()} is 0.
227 	 */
228 	public boolean isEmpty() {
229 		return size == 0;
230 	}
231 
232 	/** {@inheritDoc} */
233 	@Override
234 	public Iterator<V> iterator() {
235 		return new Iterator<V>() {
236 			private int found;
237 			private int dirIdx;
238 			private int tblIdx;
239 			private V next;
240 
241 			@Override
242 			public boolean hasNext() {
243 				return found < size;
244 			}
245 
246 			@Override
247 			public V next() {
248 				if (next != null)
249 					return found(next);
250 
251 				for (;;) {
252 					V[] table = directory[dirIdx];
253 					if (tblIdx == table.length) {
254 						if (++dirIdx >= (1 << bits))
255 							throw new NoSuchElementException();
256 						table = directory[dirIdx];
257 						tblIdx = 0;
258 					}
259 
260 					while (tblIdx < table.length) {
261 						V v = table[tblIdx++];
262 						if (v != null)
263 							return found(v);
264 					}
265 				}
266 			}
267 
268 			@SuppressWarnings("unchecked")
269 			private V found(V v) {
270 				found++;
271 				next = (V) v.next;
272 				return v;
273 			}
274 
275 			@Override
276 			public void remove() {
277 				throw new UnsupportedOperationException();
278 			}
279 		};
280 	}
281 
282 	@SuppressWarnings("unchecked")
283 	private void grow() {
284 		final int oldDirLen = 1 << bits;
285 		final int s = 1 << bits;
286 
287 		bits++;
288 		mask = (1 << bits) - 1;
289 		grow = computeGrowAt(bits);
290 
291 		// Quadruple the directory if it needs to expand. Expanding the
292 		// directory is expensive because it generates garbage, so try
293 		// to avoid doing it often.
294 		//
295 		final int newDirLen = 1 << bits;
296 		if (directory.length < newDirLen) {
297 			V[][] newDir = (V[][]) new Entry[newDirLen << 1][];
298 			System.arraycopy(directory, 0, newDir, 0, oldDirLen);
299 			directory = newDir;
300 		}
301 
302 		// For every bucket of every old segment, split the chain between
303 		// the old segment and the new segment's corresponding bucket. To
304 		// select between them use the lowest bit that was just added into
305 		// the mask above. This causes the table to double in capacity.
306 		//
307 		for (int dirIdx = 0; dirIdx < oldDirLen; dirIdx++) {
308 			final V[] oldTable = directory[dirIdx];
309 			final V[] newTable = newSegment();
310 
311 			for (int i = 0; i < oldTable.length; i++) {
312 				V chain0 = null;
313 				V chain1 = null;
314 				V next;
315 
316 				for (V obj = oldTable[i]; obj != null; obj = next) {
317 					next = (V) obj.next;
318 
319 					if ((obj.w1 & s) == 0) {
320 						obj.next = chain0;
321 						chain0 = obj;
322 					} else {
323 						obj.next = chain1;
324 						chain1 = obj;
325 					}
326 				}
327 
328 				oldTable[i] = chain0;
329 				newTable[i] = chain1;
330 			}
331 
332 			directory[oldDirLen + dirIdx] = newTable;
333 		}
334 	}
335 
336 	@SuppressWarnings("unchecked")
337 	private final V[] newSegment() {
338 		return (V[]) new Entry[1 << SEGMENT_BITS];
339 	}
340 
341 	private static final int computeGrowAt(int bits) {
342 		return 1 << (bits + SEGMENT_BITS);
343 	}
344 
345 	private static final boolean equals(AnyObjectId firstObjectId,
346 			AnyObjectId secondObjectId) {
347 		return firstObjectId.w2 == secondObjectId.w2
348 				&& firstObjectId.w3 == secondObjectId.w3
349 				&& firstObjectId.w4 == secondObjectId.w4
350 				&& firstObjectId.w5 == secondObjectId.w5
351 				&& firstObjectId.w1 == secondObjectId.w1;
352 	}
353 
354 	/** Type of entry stored in the {@link ObjectIdOwnerMap}. */
355 	public static abstract class Entry extends ObjectId {
356 		transient Entry next;
357 
358 		/**
359 		 * Initialize this entry with a specific ObjectId.
360 		 *
361 		 * @param id
362 		 *            the id the entry represents.
363 		 */
364 		public Entry(AnyObjectId id) {
365 			super(id);
366 		}
367 	}
368 }