diff options
Diffstat (limited to 'src/main/java/org/elasticsearch/common/util/BigArrays.java')
-rw-r--r-- | src/main/java/org/elasticsearch/common/util/BigArrays.java | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/src/main/java/org/elasticsearch/common/util/BigArrays.java b/src/main/java/org/elasticsearch/common/util/BigArrays.java new file mode 100644 index 0000000..77a6ee5 --- /dev/null +++ b/src/main/java/org/elasticsearch/common/util/BigArrays.java @@ -0,0 +1,459 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.common.util; + +import com.google.common.base.Preconditions; +import org.apache.lucene.util.ArrayUtil; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.RamUsageEstimator; +import org.elasticsearch.cache.recycler.PageCacheRecycler; + +import java.util.Arrays; + +/** Utility class to work with arrays. */ +public enum BigArrays { + ; + + /** Page size in bytes: 16KB */ + public static final int PAGE_SIZE_IN_BYTES = 1 << 14; + public static final int BYTE_PAGE_SIZE = BigArrays.PAGE_SIZE_IN_BYTES / RamUsageEstimator.NUM_BYTES_BYTE; + public static final int INT_PAGE_SIZE = BigArrays.PAGE_SIZE_IN_BYTES / RamUsageEstimator.NUM_BYTES_INT; + public static final int LONG_PAGE_SIZE = BigArrays.PAGE_SIZE_IN_BYTES / RamUsageEstimator.NUM_BYTES_LONG; + public static final int DOUBLE_PAGE_SIZE = BigArrays.PAGE_SIZE_IN_BYTES / RamUsageEstimator.NUM_BYTES_DOUBLE; + public static final int OBJECT_PAGE_SIZE = BigArrays.PAGE_SIZE_IN_BYTES / RamUsageEstimator.NUM_BYTES_OBJECT_REF; + + /** Returns the next size to grow when working with parallel arrays that may have different page sizes or number of bytes per element. */ + public static long overSize(long minTargetSize) { + return overSize(minTargetSize, PAGE_SIZE_IN_BYTES / 8, 1); + } + + /** Return the next size to grow to that is >= <code>minTargetSize</code>. + * Inspired from {@link ArrayUtil#oversize(int, int)} and adapted to play nicely with paging. */ + public static long overSize(long minTargetSize, int pageSize, int bytesPerElement) { + Preconditions.checkArgument(minTargetSize >= 0, "minTargetSize must be >= 0"); + Preconditions.checkArgument(pageSize >= 0, "pageSize must be > 0"); + Preconditions.checkArgument(bytesPerElement > 0, "bytesPerElement must be > 0"); + + long newSize; + if (minTargetSize < pageSize) { + newSize = ArrayUtil.oversize((int)minTargetSize, bytesPerElement); + } else { + newSize = minTargetSize + (minTargetSize >>> 3); + } + + if (newSize > pageSize) { + // round to a multiple of pageSize + newSize = newSize - (newSize % pageSize) + pageSize; + assert newSize % pageSize == 0; + } + + return newSize; + } + + static boolean indexIsInt(long index) { + return index == (int) index; + } + + private static class ByteArrayWrapper extends AbstractArray implements ByteArray { + + private final byte[] array; + + ByteArrayWrapper(byte[] array, PageCacheRecycler recycler, boolean clearOnResize) { + super(recycler, clearOnResize); + this.array = array; + } + + @Override + public long size() { + return array.length; + } + + @Override + public byte get(long index) { + assert indexIsInt(index); + return array[(int) index]; + } + + @Override + public byte set(long index, byte value) { + assert indexIsInt(index); + final byte ret = array[(int) index]; + array[(int) index] = value; + return ret; + } + + @Override + public void get(long index, int len, BytesRef ref) { + assert indexIsInt(index); + ref.bytes = array; + ref.offset = (int) index; + ref.length = len; + } + + @Override + public void set(long index, byte[] buf, int offset, int len) { + assert indexIsInt(index); + System.arraycopy(buf, offset, array, (int) index, len); + } + + } + + private static class IntArrayWrapper extends AbstractArray implements IntArray { + + private final int[] array; + + IntArrayWrapper(int[] array, PageCacheRecycler recycler, boolean clearOnResize) { + super(recycler, clearOnResize); + this.array = array; + } + + @Override + public long size() { + return array.length; + } + + @Override + public int get(long index) { + assert indexIsInt(index); + return array[(int) index]; + } + + @Override + public int set(long index, int value) { + assert indexIsInt(index); + final int ret = array[(int) index]; + array[(int) index] = value; + return ret; + } + + @Override + public int increment(long index, int inc) { + assert indexIsInt(index); + return array[(int) index] += inc; + } + + } + + private static class LongArrayWrapper extends AbstractArray implements LongArray { + + private final long[] array; + + LongArrayWrapper(long[] array, PageCacheRecycler recycler, boolean clearOnResize) { + super(recycler, clearOnResize); + this.array = array; + } + + @Override + public long size() { + return array.length; + } + + @Override + public long get(long index) { + assert indexIsInt(index); + return array[(int) index]; + } + + @Override + public long set(long index, long value) { + assert indexIsInt(index); + final long ret = array[(int) index]; + array[(int) index] = value; + return ret; + } + + @Override + public long increment(long index, long inc) { + assert indexIsInt(index); + return array[(int) index] += inc; + } + + @Override + public void fill(long fromIndex, long toIndex, long value) { + assert indexIsInt(fromIndex); + assert indexIsInt(toIndex); + Arrays.fill(array, (int) fromIndex, (int) toIndex, value); + } + } + + private static class DoubleArrayWrapper extends AbstractArray implements DoubleArray { + + private final double[] array; + + DoubleArrayWrapper(double[] array, PageCacheRecycler recycler, boolean clearOnResize) { + super(recycler, clearOnResize); + this.array = array; + } + + @Override + public long size() { + return array.length; + } + + @Override + public double get(long index) { + assert indexIsInt(index); + return array[(int) index]; + } + + @Override + public double set(long index, double value) { + assert indexIsInt(index); + double ret = array[(int) index]; + array[(int) index] = value; + return ret; + } + + @Override + public double increment(long index, double inc) { + assert indexIsInt(index); + return array[(int) index] += inc; + } + + @Override + public void fill(long fromIndex, long toIndex, double value) { + assert indexIsInt(fromIndex); + assert indexIsInt(toIndex); + Arrays.fill(array, (int) fromIndex, (int) toIndex, value); + } + + } + + private static class ObjectArrayWrapper<T> extends AbstractArray implements ObjectArray<T> { + + private final Object[] array; + + ObjectArrayWrapper(Object[] array, PageCacheRecycler recycler) { + super(recycler, true); + this.array = array; + } + + @Override + public long size() { + return array.length; + } + + @SuppressWarnings("unchecked") + @Override + public T get(long index) { + assert indexIsInt(index); + return (T) array[(int) index]; + } + + @Override + public T set(long index, T value) { + assert indexIsInt(index); + @SuppressWarnings("unchecked") + T ret = (T) array[(int) index]; + array[(int) index] = value; + return ret; + } + + } + + /** Allocate a new {@link ByteArray} of the given capacity. */ + public static ByteArray newByteArray(long size, PageCacheRecycler recycler, boolean clearOnResize) { + if (size <= BYTE_PAGE_SIZE) { + return new ByteArrayWrapper(new byte[(int) size], recycler, clearOnResize); + } else { + return new BigByteArray(size, recycler, clearOnResize); + } + } + + /** Allocate a new {@link ByteArray} of the given capacity. */ + public static ByteArray newByteArray(long size) { + return newByteArray(size, null, true); + } + + /** Resize the array to the exact provided size. */ + public static ByteArray resize(ByteArray array, long size) { + if (array instanceof BigByteArray) { + ((BigByteArray) array).resize(size); + return array; + } else { + AbstractArray arr = (AbstractArray) array; + final ByteArray newArray = newByteArray(size, arr.recycler, arr.clearOnResize); + final byte[] rawArray = ((ByteArrayWrapper) array).array; + newArray.set(0, rawArray, 0, (int) Math.min(rawArray.length, newArray.size())); + return newArray; + } + } + + /** Grow an array to a size that is larger than <code>minSize</code>, preserving content, and potentially reusing part of the provided array. */ + public static ByteArray grow(ByteArray array, long minSize) { + if (minSize <= array.size()) { + return array; + } + final long newSize = overSize(minSize, BYTE_PAGE_SIZE, RamUsageEstimator.NUM_BYTES_BYTE); + return resize(array, newSize); + } + + /** Allocate a new {@link IntArray} of the given capacity. */ + public static IntArray newIntArray(long size, PageCacheRecycler recycler, boolean clearOnResize) { + if (size <= INT_PAGE_SIZE) { + return new IntArrayWrapper(new int[(int) size], recycler, clearOnResize); + } else { + return new BigIntArray(size, recycler, clearOnResize); + } + } + + /** Allocate a new {@link IntArray} of the given capacity. */ + public static IntArray newIntArray(long size) { + return newIntArray(size, null, true); + } + + /** Resize the array to the exact provided size. */ + public static IntArray resize(IntArray array, long size) { + if (array instanceof BigIntArray) { + ((BigIntArray) array).resize(size); + return array; + } else { + AbstractArray arr = (AbstractArray) array; + final IntArray newArray = newIntArray(size, arr.recycler, arr.clearOnResize); + for (long i = 0, end = Math.min(size, array.size()); i < end; ++i) { + newArray.set(i, array.get(i)); + } + return newArray; + } + } + + /** Grow an array to a size that is larger than <code>minSize</code>, preserving content, and potentially reusing part of the provided array. */ + public static IntArray grow(IntArray array, long minSize) { + if (minSize <= array.size()) { + return array; + } + final long newSize = overSize(minSize, INT_PAGE_SIZE, RamUsageEstimator.NUM_BYTES_INT); + return resize(array, newSize); + } + + /** Allocate a new {@link LongArray} of the given capacity. */ + public static LongArray newLongArray(long size, PageCacheRecycler recycler, boolean clearOnResize) { + if (size <= LONG_PAGE_SIZE) { + return new LongArrayWrapper(new long[(int) size], recycler, clearOnResize); + } else { + return new BigLongArray(size, recycler, clearOnResize); + } + } + + /** Allocate a new {@link LongArray} of the given capacity. */ + public static LongArray newLongArray(long size) { + return newLongArray(size, null, true); + } + + /** Resize the array to the exact provided size. */ + public static LongArray resize(LongArray array, long size) { + if (array instanceof BigLongArray) { + ((BigLongArray) array).resize(size); + return array; + } else { + AbstractArray arr = (AbstractArray) array; + final LongArray newArray = newLongArray(size, arr.recycler, arr.clearOnResize); + for (long i = 0, end = Math.min(size, array.size()); i < end; ++i) { + newArray.set(i, array.get(i)); + } + return newArray; + } + } + + /** Grow an array to a size that is larger than <code>minSize</code>, preserving content, and potentially reusing part of the provided array. */ + public static LongArray grow(LongArray array, long minSize) { + if (minSize <= array.size()) { + return array; + } + final long newSize = overSize(minSize, LONG_PAGE_SIZE, RamUsageEstimator.NUM_BYTES_LONG); + return resize(array, newSize); + } + + /** Allocate a new {@link DoubleArray} of the given capacity. */ + public static DoubleArray newDoubleArray(long size, PageCacheRecycler recycler, boolean clearOnResize) { + if (size <= LONG_PAGE_SIZE) { + return new DoubleArrayWrapper(new double[(int) size], recycler, clearOnResize); + } else { + return new BigDoubleArray(size, recycler, clearOnResize); + } + } + + /** Allocate a new {@link DoubleArray} of the given capacity. */ + public static DoubleArray newDoubleArray(long size) { + return newDoubleArray(size, null, true); + } + + /** Resize the array to the exact provided size. */ + public static DoubleArray resize(DoubleArray array, long size) { + if (array instanceof BigDoubleArray) { + ((BigDoubleArray) array).resize(size); + return array; + } else { + AbstractArray arr = (AbstractArray) array; + final DoubleArray newArray = newDoubleArray(size, arr.recycler, arr.clearOnResize); + for (long i = 0, end = Math.min(size, array.size()); i < end; ++i) { + newArray.set(i, array.get(i)); + } + return newArray; + } + } + + /** Grow an array to a size that is larger than <code>minSize</code>, preserving content, and potentially reusing part of the provided array. */ + public static DoubleArray grow(DoubleArray array, long minSize) { + if (minSize <= array.size()) { + return array; + } + final long newSize = overSize(minSize, DOUBLE_PAGE_SIZE, RamUsageEstimator.NUM_BYTES_DOUBLE); + return resize(array, newSize); + } + + /** Allocate a new {@link ObjectArray} of the given capacity. */ + public static <T> ObjectArray<T> newObjectArray(long size, PageCacheRecycler recycler) { + if (size <= OBJECT_PAGE_SIZE) { + return new ObjectArrayWrapper<T>(new Object[(int) size], recycler); + } else { + return new BigObjectArray<T>(size, recycler); + } + } + + /** Allocate a new {@link ObjectArray} of the given capacity. */ + public static <T> ObjectArray<T> newObjectArray(long size) { + return newObjectArray(size, null); + } + + /** Resize the array to the exact provided size. */ + public static <T> ObjectArray<T> resize(ObjectArray<T> array, long size) { + if (array instanceof BigObjectArray) { + ((BigObjectArray<?>) array).resize(size); + return array; + } else { + final ObjectArray<T> newArray = newObjectArray(size, ((AbstractArray) array).recycler); + for (long i = 0, end = Math.min(size, array.size()); i < end; ++i) { + newArray.set(i, array.get(i)); + } + return newArray; + } + } + + /** Grow an array to a size that is larger than <code>minSize</code>, preserving content, and potentially reusing part of the provided array. */ + public static <T> ObjectArray<T> grow(ObjectArray<T> array, long minSize) { + if (minSize <= array.size()) { + return array; + } + final long newSize = overSize(minSize, OBJECT_PAGE_SIZE, RamUsageEstimator.NUM_BYTES_OBJECT_REF); + return resize(array, newSize); + } + +} |