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

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