BlockList.java
- /*
- * Copyright (C) 2011, Google Inc. and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- package org.eclipse.jgit.util;
- import java.util.AbstractList;
- import java.util.Arrays;
- import java.util.Iterator;
- import java.util.NoSuchElementException;
- /**
- * Random access list that allocates entries in blocks.
- * <p>
- * Unlike {@link java.util.ArrayList}, this type does not need to reallocate the
- * internal array in order to expand the capacity of the list. Access to any
- * element is constant time, but requires two array lookups instead of one.
- * <p>
- * To handle common usages, {@link #add(Object)} and {@link #iterator()} use
- * internal code paths to amortize out the second array lookup, making addition
- * and simple iteration closer to one array operation per element processed.
- * <p>
- * Similar to {@code ArrayList}, adding or removing from any position except the
- * end of the list requires O(N) time to copy all elements between the
- * modification point and the end of the list. Applications are strongly
- * encouraged to not use this access pattern with this list implementation.
- *
- * @param <T>
- * type of list element.
- */
- public class BlockList<T> extends AbstractList<T> {
- private static final int BLOCK_BITS = 10;
- static final int BLOCK_SIZE = 1 << BLOCK_BITS;
- private static final int BLOCK_MASK = BLOCK_SIZE - 1;
- T[][] directory;
- int size;
- private int tailDirIdx;
- private int tailBlkIdx;
- private T[] tailBlock;
- /**
- * Initialize an empty list.
- */
- public BlockList() {
- directory = BlockList.<T> newDirectory(256);
- directory[0] = BlockList.<T> newBlock();
- tailBlock = directory[0];
- }
- /**
- * Initialize an empty list with an expected capacity.
- *
- * @param capacity
- * number of elements expected to be in the list.
- */
- public BlockList(int capacity) {
- int dirSize = toDirectoryIndex(capacity);
- if ((capacity & BLOCK_MASK) != 0 || dirSize == 0)
- dirSize++;
- directory = BlockList.<T> newDirectory(dirSize);
- directory[0] = BlockList.<T> newBlock();
- tailBlock = directory[0];
- }
- /** {@inheritDoc} */
- @Override
- public int size() {
- return size;
- }
- /** {@inheritDoc} */
- @Override
- public void clear() {
- for (T[] block : directory) {
- if (block != null)
- Arrays.fill(block, null);
- }
- size = 0;
- tailDirIdx = 0;
- tailBlkIdx = 0;
- tailBlock = directory[0];
- }
- /** {@inheritDoc} */
- @Override
- public T get(int index) {
- if (index < 0 || size <= index)
- throw new IndexOutOfBoundsException(String.valueOf(index));
- return directory[toDirectoryIndex(index)][toBlockIndex(index)];
- }
- /** {@inheritDoc} */
- @Override
- public T set(int index, T element) {
- if (index < 0 || size <= index)
- throw new IndexOutOfBoundsException(String.valueOf(index));
- T[] blockRef = directory[toDirectoryIndex(index)];
- int blockIdx = toBlockIndex(index);
- T old = blockRef[blockIdx];
- blockRef[blockIdx] = element;
- return old;
- }
- /**
- * Quickly append all elements of another BlockList.
- *
- * @param src
- * the list to copy elements from.
- */
- public void addAll(BlockList<T> src) {
- if (src.size == 0)
- return;
- int srcDirIdx = 0;
- for (; srcDirIdx < src.tailDirIdx; srcDirIdx++)
- addAll(src.directory[srcDirIdx], 0, BLOCK_SIZE);
- if (src.tailBlkIdx != 0)
- addAll(src.tailBlock, 0, src.tailBlkIdx);
- }
- /**
- * Quickly append all elements from an array.
- *
- * @param src
- * the source array.
- * @param srcIdx
- * first index to copy.
- * @param srcCnt
- * number of elements to copy.
- */
- public void addAll(T[] src, int srcIdx, int srcCnt) {
- while (0 < srcCnt) {
- int i = tailBlkIdx;
- int n = Math.min(srcCnt, BLOCK_SIZE - i);
- if (n == 0) {
- // Our tail is full, expand by one.
- add(src[srcIdx++]);
- srcCnt--;
- continue;
- }
- System.arraycopy(src, srcIdx, tailBlock, i, n);
- tailBlkIdx += n;
- size += n;
- srcIdx += n;
- srcCnt -= n;
- }
- }
- /** {@inheritDoc} */
- @Override
- public boolean add(T element) {
- int i = tailBlkIdx;
- if (i < BLOCK_SIZE) {
- // Fast-path: Append to current tail block.
- tailBlock[i] = element;
- tailBlkIdx = i + 1;
- size++;
- return true;
- }
- // Slow path: Move to the next block, expanding if necessary.
- if (++tailDirIdx == directory.length) {
- T[][] newDir = BlockList.<T> newDirectory(directory.length << 1);
- System.arraycopy(directory, 0, newDir, 0, directory.length);
- directory = newDir;
- }
- T[] blockRef = directory[tailDirIdx];
- if (blockRef == null) {
- blockRef = BlockList.<T> newBlock();
- directory[tailDirIdx] = blockRef;
- }
- blockRef[0] = element;
- tailBlock = blockRef;
- tailBlkIdx = 1;
- size++;
- return true;
- }
- /** {@inheritDoc} */
- @Override
- public void add(int index, T element) {
- if (index == size) {
- // Fast-path: append onto the end of the list.
- add(element);
- } else if (index < 0 || size < index) {
- throw new IndexOutOfBoundsException(String.valueOf(index));
- } else {
- // Slow-path: the list needs to expand and insert.
- // Do this the naive way, callers shouldn't abuse
- // this class by entering this code path.
- //
- add(null); // expand the list by one
- for (int oldIdx = size - 2; index <= oldIdx; oldIdx--)
- set(oldIdx + 1, get(oldIdx));
- set(index, element);
- }
- }
- /** {@inheritDoc} */
- @Override
- public T remove(int index) {
- if (index == size - 1) {
- // Fast-path: remove the last element.
- T[] blockRef = directory[toDirectoryIndex(index)];
- int blockIdx = toBlockIndex(index);
- T old = blockRef[blockIdx];
- blockRef[blockIdx] = null;
- size--;
- if (0 < tailBlkIdx)
- tailBlkIdx--;
- else
- resetTailBlock();
- return old;
- } else if (index < 0 || size <= index) {
- throw new IndexOutOfBoundsException(String.valueOf(index));
- } else {
- // Slow-path: the list needs to contract and remove.
- // Do this the naive way, callers shouldn't abuse
- // this class by entering this code path.
- //
- T old = get(index);
- for (; index < size - 1; index++)
- set(index, get(index + 1));
- set(size - 1, null);
- size--;
- resetTailBlock();
- return old;
- }
- }
- private void resetTailBlock() {
- tailDirIdx = toDirectoryIndex(size);
- tailBlkIdx = toBlockIndex(size);
- tailBlock = directory[tailDirIdx];
- }
- /** {@inheritDoc} */
- @Override
- public Iterator<T> iterator() {
- return new MyIterator();
- }
- static final int toDirectoryIndex(int index) {
- return index >>> BLOCK_BITS;
- }
- static final int toBlockIndex(int index) {
- return index & BLOCK_MASK;
- }
- @SuppressWarnings("unchecked")
- private static <T> T[][] newDirectory(int size) {
- return (T[][]) new Object[size][];
- }
- @SuppressWarnings("unchecked")
- private static <T> T[] newBlock() {
- return (T[]) new Object[BLOCK_SIZE];
- }
- private class MyIterator implements Iterator<T> {
- private int index;
- private int dirIdx;
- private int blkIdx;
- private T[] block = directory[0];
- @Override
- public boolean hasNext() {
- return index < size;
- }
- @Override
- public T next() {
- if (size <= index)
- throw new NoSuchElementException();
- T res = block[blkIdx];
- if (++blkIdx == BLOCK_SIZE) {
- if (++dirIdx < directory.length)
- block = directory[dirIdx];
- else
- block = null;
- blkIdx = 0;
- }
- index++;
- return res;
- }
- @Override
- public void remove() {
- if (index == 0)
- throw new IllegalStateException();
- BlockList.this.remove(--index);
- dirIdx = toDirectoryIndex(index);
- blkIdx = toBlockIndex(index);
- block = directory[dirIdx];
- }
- }
- }