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