You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ObjectIdOwnerMap.java 9.9KB

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