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 8.3KB

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