/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/
/*
* 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.
*/
/*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/
package org.opensearch.common.util;
import org.apache.lucene.util.ArrayUtil;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.RamUsageEstimator;
import org.opensearch.common.Nullable;
import org.opensearch.common.recycler.Recycler;
import org.opensearch.common.lease.Releasable;
import org.opensearch.common.lease.Releasables;
import org.opensearch.core.common.breaker.CircuitBreaker;
import org.opensearch.core.common.breaker.CircuitBreakingException;
import org.opensearch.core.common.util.BigArray;
import org.opensearch.core.common.util.ByteArray;
import org.opensearch.core.indices.breaker.CircuitBreakerService;
import java.util.Arrays;
/** Utility class to work with arrays. */
public class BigArrays {
public static final BigArrays NON_RECYCLING_INSTANCE = new BigArrays(null, null, CircuitBreaker.REQUEST);
/** 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, PageCacheRecycler.PAGE_SIZE_IN_BYTES / 8, 1);
}
/** Return the next size to grow to that is >= minTargetSize
.
* Inspired from {@link ArrayUtil#oversize(int, int)} and adapted to play nicely with paging. */
public static long overSize(long minTargetSize, int pageSize, int bytesPerElement) {
if (minTargetSize < 0) {
throw new IllegalArgumentException("minTargetSize must be >= 0");
}
if (pageSize < 0) {
throw new IllegalArgumentException("pageSize must be > 0");
}
if (bytesPerElement <= 0) {
throw new IllegalArgumentException("bytesPerElement must be > 0");
}
long newSize;
if (minTargetSize < pageSize) {
newSize = Math.min(ArrayUtil.oversize((int) minTargetSize, bytesPerElement), pageSize);
} else {
final long pages = (minTargetSize + pageSize - 1) / pageSize; // ceil(minTargetSize/pageSize)
newSize = pages * pageSize;
}
return newSize;
}
static boolean indexIsInt(long index) {
return index == (int) index;
}
/**
* Base array wrapper class
*
* @opensearch.internal
*/
private abstract static class AbstractArrayWrapper extends AbstractArray implements BigArray {
static final long SHALLOW_SIZE = RamUsageEstimator.shallowSizeOfInstance(ByteArrayWrapper.class);
private final Releasable releasable;
private final long size;
AbstractArrayWrapper(BigArrays bigArrays, long size, Releasable releasable, boolean clearOnResize) {
super(bigArrays, clearOnResize);
this.releasable = releasable;
this.size = size;
}
@Override
public final long size() {
return size;
}
@Override
protected final void doClose() {
Releasables.close(releasable);
}
}
/**
* Wraps a byte array
*
* @opensearch.internal
*/
private static class ByteArrayWrapper extends AbstractArrayWrapper implements ByteArray {
private final byte[] array;
ByteArrayWrapper(BigArrays bigArrays, byte[] array, long size, Recycler.V releasable, boolean clearOnResize) {
super(bigArrays, size, releasable, clearOnResize);
this.array = array;
}
@Override
public long ramBytesUsed() {
return SHALLOW_SIZE + RamUsageEstimator.sizeOf(array);
}
@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 boolean get(long index, int len, BytesRef ref) {
assert indexIsInt(index);
ref.bytes = array;
ref.offset = (int) index;
ref.length = len;
return false;
}
@Override
public void set(long index, byte[] buf, int offset, int len) {
assert indexIsInt(index);
System.arraycopy(buf, offset, array, (int) index, len);
}
@Override
public void fill(long fromIndex, long toIndex, byte value) {
assert indexIsInt(fromIndex);
assert indexIsInt(toIndex);
Arrays.fill(array, (int) fromIndex, (int) toIndex, value);
}
@Override
public boolean hasArray() {
return true;
}
@Override
public byte[] array() {
return array;
}
}
/**
* Wraps an int array
*
* @opensearch.internal
*/
private static class IntArrayWrapper extends AbstractArrayWrapper implements IntArray {
private final int[] array;
IntArrayWrapper(BigArrays bigArrays, int[] array, long size, Recycler.V releasable, boolean clearOnResize) {
super(bigArrays, size, releasable, clearOnResize);
this.array = array;
}
@Override
public long ramBytesUsed() {
return SHALLOW_SIZE + RamUsageEstimator.sizeOf(array);
}
@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;
}
@Override
public void fill(long fromIndex, long toIndex, int value) {
assert indexIsInt(fromIndex);
assert indexIsInt(toIndex);
Arrays.fill(array, (int) fromIndex, (int) toIndex, value);
}
}
/**
* Wraps a long array
*
* @opensearch.internal
*/
private static class LongArrayWrapper extends AbstractArrayWrapper implements LongArray {
private final long[] array;
LongArrayWrapper(BigArrays bigArrays, long[] array, long size, Recycler.V releasable, boolean clearOnResize) {
super(bigArrays, size, releasable, clearOnResize);
this.array = array;
}
@Override
public long ramBytesUsed() {
return SHALLOW_SIZE + RamUsageEstimator.sizeOf(array);
}
@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);
}
}
/**
* Wraps a double array
*
* @opensearch.internal
*/
private static class DoubleArrayWrapper extends AbstractArrayWrapper implements DoubleArray {
private final long[] array;
DoubleArrayWrapper(BigArrays bigArrays, long[] array, long size, Recycler.V releasable, boolean clearOnResize) {
super(bigArrays, size, releasable, clearOnResize);
this.array = array;
}
@Override
public long ramBytesUsed() {
return SHALLOW_SIZE + RamUsageEstimator.sizeOf(array);
}
@Override
public double get(long index) {
assert indexIsInt(index);
return Double.longBitsToDouble(array[(int) index]);
}
@Override
public double set(long index, double value) {
assert indexIsInt(index);
double ret = Double.longBitsToDouble(array[(int) index]);
array[(int) index] = Double.doubleToRawLongBits(value);
return ret;
}
@Override
public double increment(long index, double inc) {
assert indexIsInt(index);
return array[(int) index] = Double.doubleToRawLongBits(Double.longBitsToDouble(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, Double.doubleToRawLongBits(value));
}
}
/**
* Wraps a float array
*
* @opensearch.internal
*/
private static class FloatArrayWrapper extends AbstractArrayWrapper implements FloatArray {
private final int[] array;
FloatArrayWrapper(BigArrays bigArrays, int[] array, long size, Recycler.V releasable, boolean clearOnResize) {
super(bigArrays, size, releasable, clearOnResize);
this.array = array;
}
@Override
public long ramBytesUsed() {
return SHALLOW_SIZE + RamUsageEstimator.sizeOf(array);
}
@Override
public float get(long index) {
assert indexIsInt(index);
return Float.intBitsToFloat(array[(int) index]);
}
@Override
public float set(long index, float value) {
assert indexIsInt(index);
float ret = Float.intBitsToFloat(array[(int) index]);
array[(int) index] = Float.floatToRawIntBits(value);
return ret;
}
@Override
public float increment(long index, float inc) {
assert indexIsInt(index);
return array[(int) index] = Float.floatToRawIntBits(Float.intBitsToFloat(array[(int) index]) + inc);
}
@Override
public void fill(long fromIndex, long toIndex, float value) {
assert indexIsInt(fromIndex);
assert indexIsInt(toIndex);
Arrays.fill(array, (int) fromIndex, (int) toIndex, Float.floatToRawIntBits(value));
}
}
/**
* Wraps an object array
*
* @opensearch.internal
*/
private static class ObjectArrayWrapper extends AbstractArrayWrapper implements ObjectArray {
private final Object[] array;
ObjectArrayWrapper(BigArrays bigArrays, Object[] array, long size, Recycler.V