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.

RefList.java 12KB

Create new RefList and RefMap utility types These types can be used by RefDatabase implementations to manage the collection. A RefList stores items sorted by their name, and is an immutable type using copy-on-write semantics to perform modifications to the collection. Binary search is used to locate an existing item by name, or to locate the proper insertion position if an item does not exist. A RefMap can merge up to 3 RefList collections at once during its entry iteration, allowing items in the resolved or loose RefList to override items by the same name in the packed RefList. The RefMap's goal is O(log N) lookup time, and O(N) iteration time, which is suitable for returning from a RefDatabase. By relying on the immutable RefList we might be able to make map construction nearly constant, making Repository.getAllRefs() an inexpensive operation if the caches are current. Since modification is not common, changes require up to O(N + log N) time to copy the internal list and collapse or expand the list's array. As most changes are made to the loose collection and not the packed collection, in practice most changes would require less than the full O(N) time, due to a significantly smaller N in the loose list. Almost complete test coverage is included in the corresponding unit tests. A handful of methods on RefMap are not tested in this change, as writing the proper test depends on a future refactoring of how the Ref class represents symbolic reference names. Change-Id: Ic2095274000336556f719edd75a5c5dd6dd1d857 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Create new RefList and RefMap utility types These types can be used by RefDatabase implementations to manage the collection. A RefList stores items sorted by their name, and is an immutable type using copy-on-write semantics to perform modifications to the collection. Binary search is used to locate an existing item by name, or to locate the proper insertion position if an item does not exist. A RefMap can merge up to 3 RefList collections at once during its entry iteration, allowing items in the resolved or loose RefList to override items by the same name in the packed RefList. The RefMap's goal is O(log N) lookup time, and O(N) iteration time, which is suitable for returning from a RefDatabase. By relying on the immutable RefList we might be able to make map construction nearly constant, making Repository.getAllRefs() an inexpensive operation if the caches are current. Since modification is not common, changes require up to O(N + log N) time to copy the internal list and collapse or expand the list's array. As most changes are made to the loose collection and not the packed collection, in practice most changes would require less than the full O(N) time, due to a significantly smaller N in the loose list. Almost complete test coverage is included in the corresponding unit tests. A handful of methods on RefMap are not tested in this change, as writing the proper test depends on a future refactoring of how the Ref class represents symbolic reference names. Change-Id: Ic2095274000336556f719edd75a5c5dd6dd1d857 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. /*
  2. * Copyright (C) 2010, 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.util;
  44. import java.util.Arrays;
  45. import java.util.Collections;
  46. import java.util.Iterator;
  47. import java.util.List;
  48. import java.util.NoSuchElementException;
  49. import org.eclipse.jgit.lib.Ref;
  50. import org.eclipse.jgit.lib.RefComparator;
  51. /**
  52. * Specialized variant of an ArrayList to support a {@code RefDatabase}.
  53. * <p>
  54. * This list is a hybrid of a Map&lt;String,Ref&gt; and of a List&lt;Ref&gt;. It
  55. * tracks reference instances by name by keeping them sorted and performing
  56. * binary search to locate an entry. Lookup time is O(log N), but addition and
  57. * removal is O(N + log N) due to the list expansion or contraction costs.
  58. * <p>
  59. * This list type is copy-on-write. Mutation methods return a new copy of the
  60. * list, leaving {@code this} unmodified. As a result we cannot easily implement
  61. * the {@link java.util.List} interface contract.
  62. *
  63. * @param <T>
  64. * the type of reference being stored in the collection.
  65. */
  66. public class RefList<T extends Ref> implements Iterable<Ref> {
  67. private static final RefList<Ref> EMPTY = new RefList<Ref>(new Ref[0], 0);
  68. /**
  69. * @return an empty unmodifiable reference list.
  70. * @param <T>
  71. * the type of reference being stored in the collection.
  72. */
  73. @SuppressWarnings("unchecked")
  74. public static <T extends Ref> RefList<T> emptyList() {
  75. return (RefList<T>) EMPTY;
  76. }
  77. private final Ref[] list;
  78. private final int cnt;
  79. RefList(Ref[] list, int cnt) {
  80. this.list = list;
  81. this.cnt = cnt;
  82. }
  83. /**
  84. * Initialize this list to use the same backing array as another list.
  85. *
  86. * @param src
  87. * the source list.
  88. */
  89. protected RefList(RefList<T> src) {
  90. this.list = src.list;
  91. this.cnt = src.cnt;
  92. }
  93. public Iterator<Ref> iterator() {
  94. return new Iterator<Ref>() {
  95. private int idx;
  96. public boolean hasNext() {
  97. return idx < cnt;
  98. }
  99. public Ref next() {
  100. if (idx < cnt)
  101. return list[idx++];
  102. throw new NoSuchElementException();
  103. }
  104. public void remove() {
  105. throw new UnsupportedOperationException();
  106. }
  107. };
  108. }
  109. /** @return this cast as an immutable, standard {@link java.util.List}. */
  110. public final List<Ref> asList() {
  111. final List<Ref> r = Arrays.asList(list).subList(0, cnt);
  112. return Collections.unmodifiableList(r);
  113. }
  114. /** @return number of items in this list. */
  115. public final int size() {
  116. return cnt;
  117. }
  118. /** @return true if the size of this list is 0. */
  119. public final boolean isEmpty() {
  120. return cnt == 0;
  121. }
  122. /**
  123. * Locate an entry by name.
  124. *
  125. * @param name
  126. * the name of the reference to find.
  127. * @return the index the reference is at. If the entry is not present
  128. * returns a negative value. The insertion position for the given
  129. * name can be computed from {@code -(index + 1)}.
  130. */
  131. public final int find(String name) {
  132. int high = cnt;
  133. if (high == 0)
  134. return -1;
  135. int low = 0;
  136. do {
  137. final int mid = (low + high) >>> 1;
  138. final int cmp = RefComparator.compareTo(list[mid], name);
  139. if (cmp < 0)
  140. low = mid + 1;
  141. else if (cmp == 0)
  142. return mid;
  143. else
  144. high = mid;
  145. } while (low < high);
  146. return -(low + 1);
  147. }
  148. /**
  149. * Determine if a reference is present.
  150. *
  151. * @param name
  152. * name of the reference to find.
  153. * @return true if the reference is present; false if it is not.
  154. */
  155. public final boolean contains(String name) {
  156. return 0 <= find(name);
  157. }
  158. /**
  159. * Get a reference object by name.
  160. *
  161. * @param name
  162. * the name of the reference.
  163. * @return the reference object; null if it does not exist in this list.
  164. */
  165. public final T get(String name) {
  166. int idx = find(name);
  167. return 0 <= idx ? get(idx) : null;
  168. }
  169. /**
  170. * Get the reference at a particular index.
  171. *
  172. * @param idx
  173. * the index to obtain. Must be {@code 0 <= idx < size()}.
  174. * @return the reference value, never null.
  175. */
  176. @SuppressWarnings("unchecked")
  177. public final T get(int idx) {
  178. return (T) list[idx];
  179. }
  180. /**
  181. * Obtain a builder initialized with the first {@code n} elements.
  182. * <p>
  183. * Copies the first {@code n} elements from this list into a new builder,
  184. * which can be used by the caller to add additional elements.
  185. *
  186. * @param n
  187. * the number of elements to copy.
  188. * @return a new builder with the first {@code n} elements already added.
  189. */
  190. public final Builder<T> copy(int n) {
  191. Builder<T> r = new Builder<T>(Math.max(16, n));
  192. r.addAll(list, 0, n);
  193. return r;
  194. }
  195. /**
  196. * Obtain a new copy of the list after changing one element.
  197. * <p>
  198. * This list instance is not affected by the replacement. Because this
  199. * method copies the entire list, it runs in O(N) time.
  200. *
  201. * @param idx
  202. * index of the element to change.
  203. * @param ref
  204. * the new value, must not be null.
  205. * @return copy of this list, after replacing {@code idx} with {@code ref} .
  206. */
  207. public final RefList<T> set(int idx, T ref) {
  208. Ref[] newList = new Ref[cnt];
  209. System.arraycopy(list, 0, newList, 0, cnt);
  210. newList[idx] = ref;
  211. return new RefList<T>(newList, cnt);
  212. }
  213. /**
  214. * Add an item at a specific index.
  215. * <p>
  216. * This list instance is not affected by the addition. Because this method
  217. * copies the entire list, it runs in O(N) time.
  218. *
  219. * @param idx
  220. * position to add the item at. If negative the method assumes it
  221. * was a direct return value from {@link #find(String)} and will
  222. * adjust it to the correct position.
  223. * @param ref
  224. * the new reference to insert.
  225. * @return copy of this list, after making space for and adding {@code ref}.
  226. */
  227. public final RefList<T> add(int idx, T ref) {
  228. if (idx < 0)
  229. idx = -(idx + 1);
  230. Ref[] newList = new Ref[cnt + 1];
  231. if (0 < idx)
  232. System.arraycopy(list, 0, newList, 0, idx);
  233. newList[idx] = ref;
  234. if (idx < cnt)
  235. System.arraycopy(list, idx, newList, idx + 1, cnt - idx);
  236. return new RefList<T>(newList, cnt + 1);
  237. }
  238. /**
  239. * Remove an item at a specific index.
  240. * <p>
  241. * This list instance is not affected by the addition. Because this method
  242. * copies the entire list, it runs in O(N) time.
  243. *
  244. * @param idx
  245. * position to remove the item from.
  246. * @return copy of this list, after making removing the item at {@code idx}.
  247. */
  248. public final RefList<T> remove(int idx) {
  249. if (cnt == 1)
  250. return emptyList();
  251. Ref[] newList = new Ref[cnt - 1];
  252. if (0 < idx)
  253. System.arraycopy(list, 0, newList, 0, idx);
  254. if (idx + 1 < cnt)
  255. System.arraycopy(list, idx + 1, newList, idx, cnt - (idx + 1));
  256. return new RefList<T>(newList, cnt - 1);
  257. }
  258. /**
  259. * Store a reference, adding or replacing as necessary.
  260. * <p>
  261. * This list instance is not affected by the store. The correct position is
  262. * determined, and the item is added if missing, or replaced if existing.
  263. * Because this method copies the entire list, it runs in O(N + log N) time.
  264. *
  265. * @param ref
  266. * the reference to store.
  267. * @return copy of this list, after performing the addition or replacement.
  268. */
  269. public final RefList<T> put(T ref) {
  270. int idx = find(ref.getName());
  271. if (0 <= idx)
  272. return set(idx, ref);
  273. return add(idx, ref);
  274. }
  275. @Override
  276. public String toString() {
  277. StringBuilder r = new StringBuilder();
  278. r.append('[');
  279. if (cnt > 0) {
  280. r.append(list[0]);
  281. for (int i = 1; i < cnt; i++) {
  282. r.append(", "); //$NON-NLS-1$
  283. r.append(list[i]);
  284. }
  285. }
  286. r.append(']');
  287. return r.toString();
  288. }
  289. /**
  290. * Builder to facilitate fast construction of an immutable RefList.
  291. *
  292. * @param <T>
  293. * type of reference being stored.
  294. */
  295. public static class Builder<T extends Ref> {
  296. private Ref[] list;
  297. private int size;
  298. /** Create an empty list ready for items to be added. */
  299. public Builder() {
  300. this(16);
  301. }
  302. /**
  303. * Create an empty list with at least the specified capacity.
  304. *
  305. * @param capacity
  306. * the new capacity.
  307. */
  308. public Builder(int capacity) {
  309. list = new Ref[capacity];
  310. }
  311. /** @return number of items in this builder's internal collection. */
  312. public int size() {
  313. return size;
  314. }
  315. /**
  316. * Get the reference at a particular index.
  317. *
  318. * @param idx
  319. * the index to obtain. Must be {@code 0 <= idx < size()}.
  320. * @return the reference value, never null.
  321. */
  322. @SuppressWarnings("unchecked")
  323. public T get(int idx) {
  324. return (T) list[idx];
  325. }
  326. /**
  327. * Remove an item at a specific index.
  328. *
  329. * @param idx
  330. * position to remove the item from.
  331. */
  332. public void remove(int idx) {
  333. System.arraycopy(list, idx + 1, list, idx, size - (idx + 1));
  334. size--;
  335. }
  336. /**
  337. * Add the reference to the end of the array.
  338. * <p>
  339. * References must be added in sort order, or the array must be sorted
  340. * after additions are complete using {@link #sort()}.
  341. *
  342. * @param ref
  343. */
  344. public void add(T ref) {
  345. if (list.length == size) {
  346. Ref[] n = new Ref[size * 2];
  347. System.arraycopy(list, 0, n, 0, size);
  348. list = n;
  349. }
  350. list[size++] = ref;
  351. }
  352. /**
  353. * Add all items from a source array.
  354. * <p>
  355. * References must be added in sort order, or the array must be sorted
  356. * after additions are complete using {@link #sort()}.
  357. *
  358. * @param src
  359. * the source array.
  360. * @param off
  361. * position within {@code src} to start copying from.
  362. * @param cnt
  363. * number of items to copy from {@code src}.
  364. */
  365. public void addAll(Ref[] src, int off, int cnt) {
  366. if (list.length < size + cnt) {
  367. Ref[] n = new Ref[Math.max(size * 2, size + cnt)];
  368. System.arraycopy(list, 0, n, 0, size);
  369. list = n;
  370. }
  371. System.arraycopy(src, off, list, size, cnt);
  372. size += cnt;
  373. }
  374. /**
  375. * Replace a single existing element.
  376. *
  377. * @param idx
  378. * index, must have already been added previously.
  379. * @param ref
  380. * the new reference.
  381. */
  382. public void set(int idx, T ref) {
  383. list[idx] = ref;
  384. }
  385. /** Sort the list's backing array in-place. */
  386. public void sort() {
  387. Arrays.sort(list, 0, size, RefComparator.INSTANCE);
  388. }
  389. /** @return an unmodifiable list using this collection's backing array. */
  390. public RefList<T> toRefList() {
  391. return new RefList<T>(list, size);
  392. }
  393. @Override
  394. public String toString() {
  395. return toRefList().toString();
  396. }
  397. }
  398. }