net.openhft.chronicle.bytes.ref.BinaryIntArrayReference Maven / Gradle / Ivy
/*
* Copyright (c) 2016-2022 chronicle.software
*
* https://chronicle.software
*
* Licensed 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 net.openhft.chronicle.bytes.ref;
import net.openhft.chronicle.bytes.*;
import net.openhft.chronicle.core.annotation.NonNegative;
import net.openhft.chronicle.core.io.ClosedIllegalStateException;
import net.openhft.chronicle.core.io.IORuntimeException;
import net.openhft.chronicle.core.io.ThreadingIllegalStateException;
import net.openhft.chronicle.core.values.IntValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.WeakReference;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Set;
import static net.openhft.chronicle.bytes.HexDumpBytes.MASK;
import static net.openhft.chronicle.bytes.ref.BinaryIntReference.INT_NOT_COMPLETE;
/**
* Represents a binary array of integers, backed by a {@link BytesStore}.
*
* This class provides operations to access and manipulate an array of integers in binary form.
* The integers are stored in a BytesStore, and this class provides various methods to perform
* atomic operations, read/write values, and manage the state of the array.
*
* The BinaryIntArrayReference class also contains the ability to throw exceptions in cases
* of buffer underflows or illegal states, and to read the array in a volatile fashion,
* ensuring a happens-before relationship between threads.
*
* Example usage:
*
* BytesStore bytesStore = ...
* BinaryIntArrayReference arrayRef = new BinaryIntArrayReference();
* arrayRef.bytesStore(bytesStore, 0, bytesStore.capacity());
* arrayRef.setOrderedValueAt(5, 12345);
* int value = arrayRef.getValueAt(5);
*
*
* Note: This class is not thread-safe, and external synchronization may be necessary if instances
* are shared between threads. The data referenced is thread safe when the appropriate methods are used.
*/
@SuppressWarnings("rawtypes")
public class BinaryIntArrayReference extends AbstractReference implements ByteableIntArrayValues, BytesMarshallable {
public static final int SHIFT = 2;
private static final long CAPACITY = 0;
private static final long USED = CAPACITY + Long.BYTES;
private static final long VALUES = USED + Long.BYTES;
private static final int MAX_TO_STRING = 1024;
@Nullable
private static Set> binaryIntArrayReferences = null;
private long length;
/**
* Default constructor initializes the BinaryIntArrayReference with a default capacity of 0.
*/
public BinaryIntArrayReference() {
this(0);
}
/**
* Constructs a new BinaryIntArrayReference with the specified default capacity.
*
* @param defaultCapacity the default capacity of the array.
*/
public BinaryIntArrayReference(long defaultCapacity) {
this.length = (defaultCapacity << SHIFT) + VALUES;
}
/**
* Initializes the collection that keeps references to BinaryIntArrayReference instances.
*/
public static void startCollecting() {
binaryIntArrayReferences = Collections.newSetFromMap(new IdentityHashMap<>());
}
/**
* Forces all BinaryIntArrayReferences to a not complete state.
*
* @throws BufferOverflowException If buffer overflows.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way
*/
public static void forceAllToNotCompleteState()
throws IllegalStateException, BufferOverflowException {
if (binaryIntArrayReferences == null)
return;
for (WeakReference x : binaryIntArrayReferences) {
@Nullable BinaryIntArrayReference binaryLongReference = x.get();
if (binaryLongReference != null) {
binaryLongReference.setValueAt(0, INT_NOT_COMPLETE);
}
}
binaryIntArrayReferences = null;
}
/**
* Initializes the binary data in the provided Bytes object with the given capacity.
*
* @param bytes the Bytes object to be written.
* @param capacity the capacity to be set.
* @throws BufferOverflowException If buffer overflows.
* @throws IllegalArgumentException If an illegal argument is encountered.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way
*/
public static void write(@NotNull Bytes> bytes, @NonNegative long capacity)
throws BufferOverflowException, IllegalArgumentException, IllegalStateException {
assert (bytes.writePosition() & 0x7) == 0;
bytes.writeLong(capacity);
bytes.writeLong(0L); // used
long start = bytes.writePosition();
bytes.zeroOut(start, start + (capacity << SHIFT));
bytes.writeSkip(capacity << SHIFT);
}
/**
* Lazily initializes the binary data in the provided Bytes object with the given capacity.
*
* @param bytes the Bytes object to be written.
* @param capacity the capacity to be set.
* @throws BufferOverflowException If buffer overflows.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way
*/
public static void lazyWrite(@NotNull Bytes> bytes, @NonNegative long capacity)
throws BufferOverflowException, IllegalStateException {
assert (bytes.writePosition() & 0x7) == 0;
bytes.writeLong(capacity);
bytes.writeLong(0L); // used
bytes.writeSkip(capacity << SHIFT);
}
/**
* Calculates and returns the peak length from the BytesStore at the given offset.
*
* @param bytes the BytesStore object to read from.
* @param offset the offset in the BytesStore to start reading from.
* @return the peak length.
* @throws BufferUnderflowException If buffer underflows.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException if this resource was accessed by multiple threads in an unsafe way
*/
public static long peakLength(@NotNull BytesStore, ?> bytes, @NonNegative long offset)
throws BufferUnderflowException, IllegalStateException {
final long capacity = bytes.readLong(offset + CAPACITY);
assert capacity > 0 : "capacity too small " + capacity;
return (capacity << SHIFT) + VALUES;
}
/**
* Assigns a new BytesStore to this BinaryIntArrayReference.
*
* @param bytes the new BytesStore to be assigned.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException if this resource was accessed by multiple threads in an unsafe way
*/
@Override
protected void acceptNewBytesStore(@NotNull final BytesStore, ?> bytes)
throws IllegalStateException {
if (this.bytesStore != null) {
this.bytesStore.release(this);
}
this.bytesStore = bytes;
this.bytesStore.reserve(this);
}
/**
* Gets the capacity of the array.
*
* @return the capacity.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException if this resource was accessed by multiple threads in an unsafe way
*/
@Override
public long getCapacity()
throws IllegalStateException {
throwExceptionIfClosed();
return (length - VALUES) >>> SHIFT;
}
/**
* Gets the number of used elements in the array.
*
* @return the number of used elements.
* @throws BufferUnderflowException If buffer underflows.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way
*/
@Override
public long getUsed()
throws IllegalStateException, BufferUnderflowException {
throwExceptionIfClosed();
return bytesStore.readVolatileInt(offset + USED);
}
/**
* Sets the maximum number of used elements in the array.
*
* @param usedAtLeast the number of used elements to be set.
* @throws BufferUnderflowException If buffer underflows.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way
*/
@Override
public void setMaxUsed(long usedAtLeast)
throws IllegalStateException, BufferUnderflowException {
throwExceptionIfClosedInSetter();
bytesStore.writeMaxLong(offset + USED, usedAtLeast);
}
/**
* Gets the value at the specified index.
*
* @param index the index to retrieve the value from.
* @return the value at the specified index.
* @throws BufferUnderflowException If buffer underflows.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way
*/
@Override
public int getValueAt(@NonNegative long index)
throws IllegalStateException, BufferUnderflowException {
throwExceptionIfClosed();
return bytesStore.readInt(VALUES + offset + (index << SHIFT));
}
/**
* Sets the value at the specified index.
*
* @param index the index to set the value at.
* @param value the value to be set.
* @throws BufferOverflowException If buffer overflows.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way
*/
@Override
public void setValueAt(@NonNegative long index, int value)
throws IllegalStateException, BufferOverflowException {
throwExceptionIfClosedInSetter();
bytesStore.writeInt(VALUES + offset + (index << SHIFT), value);
}
/**
* Retrieves the value at the specified index with volatile semantics.
*
* @param index the index to retrieve the value from.
* @return the value at the specified index.
* @throws BufferUnderflowException If buffer underflows.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way
*/
@Override
public int getVolatileValueAt(@NonNegative long index)
throws IllegalStateException, BufferUnderflowException {
throwExceptionIfClosed();
return bytesStore.readVolatileInt(VALUES + offset + (index << SHIFT));
}
/**
* Binds the value at the specified index to the provided IntValue.
*
* @param index the index to bind the value at.
* @param value the IntValue to be bound.
* @throws BufferOverflowException If buffer overflows.
* @throws IllegalArgumentException If the arguments are invalid.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way
*/
@Override
public void bindValueAt(@NonNegative long index, @NotNull IntValue value)
throws IllegalStateException, BufferOverflowException, IllegalArgumentException {
throwExceptionIfClosed();
((BinaryIntReference) value).bytesStore(bytesStore, VALUES + offset + (index << SHIFT), 8);
}
/**
* Sets the value at the specified index with ordered semantics.
*
* @param index the index to set the value at.
* @param value the value to be set.
* @throws BufferOverflowException If buffer overflows.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException if this resource was accessed by multiple threads in an unsafe way
*/
@Override
public void setOrderedValueAt(@NonNegative long index, int value)
throws BufferOverflowException, IllegalStateException {
throwExceptionIfClosedInSetter();
bytesStore.writeOrderedInt(VALUES + offset + (index << SHIFT), value);
}
/**
* Stores a bytes sequence into the BinaryIntArrayReference.
*
* @param bytes the bytes sequence to store.
* @param offset the starting position.
* @param length the length of bytes sequence.
* @throws IllegalArgumentException If the length does not match the peak length.
* @throws BufferOverflowException If buffer overflows.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way
*/
@Override
public void bytesStore(@NotNull BytesStore bytes, @NonNegative long offset, @NonNegative long length)
throws IllegalArgumentException, IllegalStateException, BufferOverflowException {
throwExceptionIfClosed();
long peakLength = peakLength(bytes, offset);
if (length != peakLength)
throw new IllegalArgumentException(length + " != " + peakLength);
if (bytes instanceof HexDumpBytes) {
offset &= MASK;
}
assert (offset & 7) == 0 : "offset=" + offset;
super.bytesStore(bytes, (offset + 7) & ~7, length);
this.length = length;
}
/**
* Reads and deserializes data from the input stream.
*
* @param bytes the input stream.
* @throws IORuntimeException If an IO exception occurs.
* @throws BufferUnderflowException If buffer underflow occurs.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way
*/
@Override
public void readMarshallable(BytesIn> bytes)
throws IORuntimeException, IllegalStateException, BufferUnderflowException {
throwExceptionIfClosedInSetter();
long position = bytes.readPosition();
long capacity = bytes.readLong();
long used = bytes.readLong();
if (capacity < 0 || capacity > bytes.readRemaining() >> SHIFT)
throw new IORuntimeException("Corrupt used capacity");
if (used < 0 || used > capacity)
throw new IORuntimeException("Corrupt used value");
bytes.readSkip(capacity << SHIFT);
long len = bytes.readPosition() - position;
bytesStore((Bytes) bytes, position, len);
}
/**
* Serializes and writes data to the output stream.
*
* @param bytes the output stream.
* @throws BufferOverflowException If buffer overflow occurs.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way
*/
@Override
public void writeMarshallable(BytesOut> bytes)
throws IllegalStateException, BufferOverflowException {
final boolean retainsComments = bytes.retainedHexDumpDescription();
if (retainsComments)
bytes.writeHexDumpDescription("BinaryIntArrayReference");
BytesStore, ?> bytesStore = bytesStore();
if (bytesStore == null) {
long capacity = getCapacity();
if (retainsComments)
bytes.writeHexDumpDescription("capacity");
bytes.writeLong(capacity);
if (retainsComments)
bytes.writeHexDumpDescription("used");
bytes.writeLong(0);
if (retainsComments)
bytes.writeHexDumpDescription("values");
bytes.writeSkip(capacity << SHIFT);
} else {
bytes.write(bytesStore, offset, length);
}
}
/**
* Checks if the instance is null.
*
* @return true if the instance is null, false otherwise.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException if this resource was accessed by multiple threads in an unsafe way
*/
@Override
public boolean isNull()
throws IllegalStateException {
throwExceptionIfClosed();
return bytesStore == null;
}
/**
* Resets the instance to its initial state.
*
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException if this resource was accessed by multiple threads in an unsafe way
*/
@Override
public void reset()
throws IllegalStateException {
throwExceptionIfClosedInSetter();
bytesStore = null;
offset = 0;
length = 0;
}
/**
* Retrieves the BytesStore.
*
* @return the BytesStore, or null if not set.
*/
@Nullable
@Override
public BytesStore, ?> bytesStore() {
return bytesStore;
}
/**
* Retrieves the offset position.
*
* @return the offset position.
*/
@Override
public long offset() {
return offset;
}
/**
* Retrieves the maximum size.
*
* @return the maximum size.
*/
@Override
public long maxSize() {
return length;
}
/**
* Returns a string representation of the BinaryIntArrayReference.
*
* @return a string representation.
*/
@NotNull
@Override
public String toString() {
if (bytesStore == null) {
return "not set";
}
StringBuilder sb = new StringBuilder();
sb.append("used: ");
try {
long used = getUsed();
sb.append(used);
sb.append(", value: ");
appendContents(sb, used);
return sb.toString();
} catch (Exception e) {
return e.toString();
}
}
/**
* Appends the contents to the provided StringBuilder.
*
* @param sb the StringBuilder to append to.
* @param used the number of used elements.
*/
private void appendContents(@NotNull StringBuilder sb, long used) {
String sep = "";
try {
int i;
int max = (int) Math.min(used, Math.min(getCapacity(), MAX_TO_STRING));
for (i = 0; i < max; i++) {
long valueAt = getValueAt(i);
sb.append(sep).append(valueAt);
sep = ", ";
}
if (i < getCapacity())
sb.append(" ...");
} catch (BufferUnderflowException e) {
sb.append(" ").append(e);
}
}
/**
* Calculates the size in bytes of the array with the given capacity.
*
* @param capacity the capacity of the array.
* @return the size in bytes.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException if this resource was accessed by multiple threads in an unsafe way
*/
@Override
public long sizeInBytes(@NonNegative long capacity)
throws IllegalStateException {
throwExceptionIfClosed();
return (capacity << SHIFT) + VALUES;
}
/**
* Sets the capacity of the BinaryIntArrayReference.
*
* @param arrayLength the desired capacity.
* @return this BinaryIntArrayReference with the updated capacity.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException if this resource was accessed by multiple threads in an unsafe way
*/
@Override
public ByteableIntArrayValues capacity(long arrayLength)
throws IllegalStateException {
throwExceptionIfClosedInSetter();
BytesStore, ?> bytesStore = bytesStore();
long len = sizeInBytes(arrayLength);
if (bytesStore == null) {
this.length = len;
} else {
assert this.length == len;
}
return this;
}
/**
* Atomically sets the value at the specified index to the given updated value
* if the current value equals the expected value.
*
* @param index the index of the value to be updated.
* @param expected the expected value.
* @param value the new value.
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
* @throws BufferOverflowException If buffer overflow occurs.
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException if this resource was accessed by multiple threads in an unsafe way
*/
@Override
public boolean compareAndSet(@NonNegative long index, int expected, int value)
throws BufferOverflowException, IllegalStateException {
throwExceptionIfClosed();
if (value == INT_NOT_COMPLETE && binaryIntArrayReferences != null)
binaryIntArrayReferences.add(new WeakReference<>(this));
return bytesStore.compareAndSwapInt(VALUES + offset + (index << SHIFT), expected, value);
}
}