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.

BlockList.java 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  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.util;
  44. import java.util.AbstractList;
  45. import java.util.Arrays;
  46. import java.util.Iterator;
  47. import java.util.NoSuchElementException;
  48. /**
  49. * Random access list that allocates entries in blocks.
  50. * <p>
  51. * Unlike {@link java.util.ArrayList}, this type does not need to reallocate the
  52. * internal array in order to expand the capacity of the list. Access to any
  53. * element is constant time, but requires two array lookups instead of one.
  54. * <p>
  55. * To handle common usages, {@link #add(Object)} and {@link #iterator()} use
  56. * internal code paths to amortize out the second array lookup, making addition
  57. * and simple iteration closer to one array operation per element processed.
  58. * <p>
  59. * Similar to {@code ArrayList}, adding or removing from any position except the
  60. * end of the list requires O(N) time to copy all elements between the
  61. * modification point and the end of the list. Applications are strongly
  62. * encouraged to not use this access pattern with this list implementation.
  63. *
  64. * @param <T>
  65. * type of list element.
  66. */
  67. public class BlockList<T> extends AbstractList<T> {
  68. private static final int BLOCK_BITS = 10;
  69. static final int BLOCK_SIZE = 1 << BLOCK_BITS;
  70. private static final int BLOCK_MASK = BLOCK_SIZE - 1;
  71. T[][] directory;
  72. int size;
  73. private int tailDirIdx;
  74. private int tailBlkIdx;
  75. private T[] tailBlock;
  76. /**
  77. * Initialize an empty list.
  78. */
  79. public BlockList() {
  80. directory = BlockList.<T> newDirectory(256);
  81. directory[0] = BlockList.<T> newBlock();
  82. tailBlock = directory[0];
  83. }
  84. /**
  85. * Initialize an empty list with an expected capacity.
  86. *
  87. * @param capacity
  88. * number of elements expected to be in the list.
  89. */
  90. public BlockList(int capacity) {
  91. int dirSize = toDirectoryIndex(capacity);
  92. if ((capacity & BLOCK_MASK) != 0 || dirSize == 0)
  93. dirSize++;
  94. directory = BlockList.<T> newDirectory(dirSize);
  95. directory[0] = BlockList.<T> newBlock();
  96. tailBlock = directory[0];
  97. }
  98. /** {@inheritDoc} */
  99. @Override
  100. public int size() {
  101. return size;
  102. }
  103. /** {@inheritDoc} */
  104. @Override
  105. public void clear() {
  106. for (T[] block : directory) {
  107. if (block != null)
  108. Arrays.fill(block, null);
  109. }
  110. size = 0;
  111. tailDirIdx = 0;
  112. tailBlkIdx = 0;
  113. tailBlock = directory[0];
  114. }
  115. /** {@inheritDoc} */
  116. @Override
  117. public T get(int index) {
  118. if (index < 0 || size <= index)
  119. throw new IndexOutOfBoundsException(String.valueOf(index));
  120. return directory[toDirectoryIndex(index)][toBlockIndex(index)];
  121. }
  122. /** {@inheritDoc} */
  123. @Override
  124. public T set(int index, T element) {
  125. if (index < 0 || size <= index)
  126. throw new IndexOutOfBoundsException(String.valueOf(index));
  127. T[] blockRef = directory[toDirectoryIndex(index)];
  128. int blockIdx = toBlockIndex(index);
  129. T old = blockRef[blockIdx];
  130. blockRef[blockIdx] = element;
  131. return old;
  132. }
  133. /**
  134. * Quickly append all elements of another BlockList.
  135. *
  136. * @param src
  137. * the list to copy elements from.
  138. */
  139. public void addAll(BlockList<T> src) {
  140. if (src.size == 0)
  141. return;
  142. int srcDirIdx = 0;
  143. for (; srcDirIdx < src.tailDirIdx; srcDirIdx++)
  144. addAll(src.directory[srcDirIdx], 0, BLOCK_SIZE);
  145. if (src.tailBlkIdx != 0)
  146. addAll(src.tailBlock, 0, src.tailBlkIdx);
  147. }
  148. /**
  149. * Quickly append all elements from an array.
  150. *
  151. * @param src
  152. * the source array.
  153. * @param srcIdx
  154. * first index to copy.
  155. * @param srcCnt
  156. * number of elements to copy.
  157. */
  158. public void addAll(T[] src, int srcIdx, int srcCnt) {
  159. while (0 < srcCnt) {
  160. int i = tailBlkIdx;
  161. int n = Math.min(srcCnt, BLOCK_SIZE - i);
  162. if (n == 0) {
  163. // Our tail is full, expand by one.
  164. add(src[srcIdx++]);
  165. srcCnt--;
  166. continue;
  167. }
  168. System.arraycopy(src, srcIdx, tailBlock, i, n);
  169. tailBlkIdx += n;
  170. size += n;
  171. srcIdx += n;
  172. srcCnt -= n;
  173. }
  174. }
  175. /** {@inheritDoc} */
  176. @Override
  177. public boolean add(T element) {
  178. int i = tailBlkIdx;
  179. if (i < BLOCK_SIZE) {
  180. // Fast-path: Append to current tail block.
  181. tailBlock[i] = element;
  182. tailBlkIdx = i + 1;
  183. size++;
  184. return true;
  185. }
  186. // Slow path: Move to the next block, expanding if necessary.
  187. if (++tailDirIdx == directory.length) {
  188. T[][] newDir = BlockList.<T> newDirectory(directory.length << 1);
  189. System.arraycopy(directory, 0, newDir, 0, directory.length);
  190. directory = newDir;
  191. }
  192. T[] blockRef = directory[tailDirIdx];
  193. if (blockRef == null) {
  194. blockRef = BlockList.<T> newBlock();
  195. directory[tailDirIdx] = blockRef;
  196. }
  197. blockRef[0] = element;
  198. tailBlock = blockRef;
  199. tailBlkIdx = 1;
  200. size++;
  201. return true;
  202. }
  203. /** {@inheritDoc} */
  204. @Override
  205. public void add(int index, T element) {
  206. if (index == size) {
  207. // Fast-path: append onto the end of the list.
  208. add(element);
  209. } else if (index < 0 || size < index) {
  210. throw new IndexOutOfBoundsException(String.valueOf(index));
  211. } else {
  212. // Slow-path: the list needs to expand and insert.
  213. // Do this the naive way, callers shouldn't abuse
  214. // this class by entering this code path.
  215. //
  216. add(null); // expand the list by one
  217. for (int oldIdx = size - 2; index <= oldIdx; oldIdx--)
  218. set(oldIdx + 1, get(oldIdx));
  219. set(index, element);
  220. }
  221. }
  222. /** {@inheritDoc} */
  223. @Override
  224. public T remove(int index) {
  225. if (index == size - 1) {
  226. // Fast-path: remove the last element.
  227. T[] blockRef = directory[toDirectoryIndex(index)];
  228. int blockIdx = toBlockIndex(index);
  229. T old = blockRef[blockIdx];
  230. blockRef[blockIdx] = null;
  231. size--;
  232. if (0 < tailBlkIdx)
  233. tailBlkIdx--;
  234. else
  235. resetTailBlock();
  236. return old;
  237. } else if (index < 0 || size <= index) {
  238. throw new IndexOutOfBoundsException(String.valueOf(index));
  239. } else {
  240. // Slow-path: the list needs to contract and remove.
  241. // Do this the naive way, callers shouldn't abuse
  242. // this class by entering this code path.
  243. //
  244. T old = get(index);
  245. for (; index < size - 1; index++)
  246. set(index, get(index + 1));
  247. set(size - 1, null);
  248. size--;
  249. resetTailBlock();
  250. return old;
  251. }
  252. }
  253. private void resetTailBlock() {
  254. tailDirIdx = toDirectoryIndex(size);
  255. tailBlkIdx = toBlockIndex(size);
  256. tailBlock = directory[tailDirIdx];
  257. }
  258. /** {@inheritDoc} */
  259. @Override
  260. public Iterator<T> iterator() {
  261. return new MyIterator();
  262. }
  263. static final int toDirectoryIndex(int index) {
  264. return index >>> BLOCK_BITS;
  265. }
  266. static final int toBlockIndex(int index) {
  267. return index & BLOCK_MASK;
  268. }
  269. @SuppressWarnings("unchecked")
  270. private static <T> T[][] newDirectory(int size) {
  271. return (T[][]) new Object[size][];
  272. }
  273. @SuppressWarnings("unchecked")
  274. private static <T> T[] newBlock() {
  275. return (T[]) new Object[BLOCK_SIZE];
  276. }
  277. private class MyIterator implements Iterator<T> {
  278. private int index;
  279. private int dirIdx;
  280. private int blkIdx;
  281. private T[] block = directory[0];
  282. @Override
  283. public boolean hasNext() {
  284. return index < size;
  285. }
  286. @Override
  287. public T next() {
  288. if (size <= index)
  289. throw new NoSuchElementException();
  290. T res = block[blkIdx];
  291. if (++blkIdx == BLOCK_SIZE) {
  292. if (++dirIdx < directory.length)
  293. block = directory[dirIdx];
  294. else
  295. block = null;
  296. blkIdx = 0;
  297. }
  298. index++;
  299. return res;
  300. }
  301. @Override
  302. public void remove() {
  303. if (index == 0)
  304. throw new IllegalStateException();
  305. BlockList.this.remove(--index);
  306. dirIdx = toDirectoryIndex(index);
  307. blkIdx = toBlockIndex(index);
  308. block = directory[dirIdx];
  309. }
  310. }
  311. }