net.openhft.chronicle.bytes.UncheckedBytes 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;
import net.openhft.chronicle.bytes.internal.BytesInternal;
import net.openhft.chronicle.bytes.internal.ReferenceCountedUtil;
import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.OS;
import net.openhft.chronicle.core.annotation.NonNegative;
import net.openhft.chronicle.core.io.ClosedIllegalStateException;
import net.openhft.chronicle.core.io.ThreadingIllegalStateException;
import net.openhft.chronicle.core.util.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import static java.util.Objects.requireNonNull;
import static net.openhft.chronicle.core.Jvm.uncheckedCast;
import static net.openhft.chronicle.core.util.Longs.requireNonNegative;
import static net.openhft.chronicle.core.util.StringUtils.extractBytes;
import static net.openhft.chronicle.core.util.StringUtils.extractChars;
/**
* An optimized extension of AbstractBytes that doesn't perform any bounds checking
* for read and write operations. This class is designed for scenarios where speed is crucial,
* and the client is certain that all operations are within valid bounds, therefore, skipping
* the overhead of bounds checking.
*
* Warning: Using this class improperly can result in IndexOutOfBoundsException being thrown
* or worse, it can corrupt your data, cause JVM crashes, or produce other undefined behavior.
*/
@SuppressWarnings("rawtypes")
public class UncheckedBytes
extends AbstractBytes {
// The underlying Bytes instance this UncheckedBytes wraps around
private Bytes> underlyingBytes;
/**
* Constructs an UncheckedBytes instance by wrapping around the provided Bytes object.
*
* @param underlyingBytes the Bytes object to wrap around
* @throws ClosedIllegalStateException If the resource has been released or closed.
* @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way
*/
@SuppressWarnings("this-escape")
public UncheckedBytes(@NotNull Bytes> underlyingBytes)
throws IllegalStateException {
super(uncheckedCast(requireNonNull(underlyingBytes.bytesStore())),
underlyingBytes.writePosition(),
Math.min(underlyingBytes.writeLimit(), underlyingBytes.realCapacity()));
this.underlyingBytes = underlyingBytes;
readPosition(underlyingBytes.readPosition());
if (writeLimit > capacity())
writeLimit(capacity());
}
/**
* Sets the underlying Bytes instance for this UncheckedBytes.
* Releases any resources associated with the current underlying BytesStore, and
* reserves the BytesStore of the new underlying Bytes.
*
* @param bytes the new underlying Bytes instance
* @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 void setBytes(@NotNull Bytes> bytes)
throws IllegalStateException {
requireNonNull(bytes);
final BytesStore, ?> underlying = bytes.bytesStore();
if (bytesStore != underlying) {
bytesStore.release(this);
this.bytesStore(uncheckedCast(underlying));
bytesStore.reserve(this);
}
readPosition(bytes.readPosition());
this.uncheckedWritePosition(bytes.writePosition());
this.writeLimit = bytes.writeLimit();
this.underlyingBytes = bytes;
}
@Override
public void ensureCapacity(@NonNegative long desiredCapacity)
throws IllegalArgumentException, IllegalStateException {
if (desiredCapacity > realCapacity()) {
underlyingBytes.ensureCapacity(desiredCapacity);
bytesStore(uncheckedCast(underlyingBytes.bytesStore()));
}
}
@Override
@NotNull
public Bytes unchecked(boolean unchecked) {
throwExceptionIfReleased();
return this;
}
@Override
public boolean unchecked() {
return true;
}
@Override
protected void writeCheckOffset(@NonNegative long offset, @NonNegative long adding) {
// Do nothing
}
@Override
protected void readCheckOffset(@NonNegative long offset, long adding, boolean given) {
// Do nothing
}
@Override
void prewriteCheckOffset(@NonNegative long offset, long subtracting) {
// Do nothing
}
@NotNull
@Override
public Bytes readPosition(@NonNegative long position) {
readPosition = position;
return this;
}
@NotNull
@Override
public Bytes readLimit(@NonNegative long limit) {
uncheckedWritePosition(limit);
return this;
}
@NotNull
@Override
public Bytes writePosition(@NonNegative long position) {
uncheckedWritePosition(position);
return this;
}
@NotNull
@Override
public Bytes readSkip(long bytesToSkip) {
readPosition += bytesToSkip;
return this;
}
@NotNull
@Override
public Bytes writeSkip(long bytesToSkip) {
uncheckedWritePosition(writePosition() + bytesToSkip);
return this;
}
@NotNull
@Override
public Bytes writeLimit(@NonNegative long limit) {
writeLimit = limit;
return this;
}
@NotNull
@Override
public BytesStore, U> copy() {
throwExceptionIfReleased();
throw new UnsupportedOperationException("todo");
}
@Override
public boolean isElastic() {
return false;
}
@Override
protected long readOffsetPositionMoved(@NonNegative long adding) {
long offset = readPosition;
readPosition += adding;
return offset;
}
@Override
protected long writeOffsetPositionMoved(@NonNegative long adding, @NonNegative long advance) {
long oldPosition = writePosition();
uncheckedWritePosition(writePosition() + advance);
return oldPosition;
}
@Override
protected long prewriteOffsetPositionMoved(@NonNegative long subtracting)
throws BufferOverflowException {
readPosition -= subtracting;
return readPosition;
}
@NotNull
@Override
public Bytes write(@NotNull BytesStore, ?> bytes, @NonNegative long offset, @NonNegative long length)
throws BufferOverflowException, IllegalArgumentException, IllegalStateException, BufferUnderflowException {
ReferenceCountedUtil.throwExceptionIfReleased(bytes);
requireNonNegative(offset);
requireNonNegative(length);
if (length == 8) {
writeLong(bytes.readLong(offset));
} else if (bytes.underlyingObject() == null
&& bytesStore
.isDirectMemory() &&
length >= 32) {
rawCopy(bytes, offset, length);
} else {
super.write(bytes, offset, length);
}
return this;
}
@Override
@NotNull
public Bytes append8bit(@NotNull CharSequence cs)
throws BufferOverflowException, BufferUnderflowException, IllegalStateException {
requireNonNull(cs);
int length = cs.length();
long offset = writeOffsetPositionMoved(length);
for (int i = 0; i < length; i++) {
char c = cs.charAt(i);
if (c > 255) c = '?';
writeByte(offset++, (byte) c);
}
return this;
}
long rawCopy(@NotNull BytesStore, ?> bytes, @NonNegative long offset, @NonNegative long length)
throws BufferOverflowException, IllegalStateException, BufferUnderflowException {
requireNonNull(bytes);
long len = Math.min(writeRemaining(), Math.min(bytes.capacity() - offset, length));
if (len > 0) {
writeCheckOffset(writePosition(), len);
this.throwExceptionIfReleased();
OS.memory().copyMemory(bytes.addressForRead(offset), addressForWritePosition(), len);
writeSkip(len);
}
return len;
}
@NotNull
@Override
public Bytes writeByte(byte i8)
throws BufferOverflowException, IllegalStateException {
long offset = writeOffsetPositionMoved(1);
bytesStore.writeByte(offset, i8);
return this;
}
@NotNull
@Override
public Bytes writeUtf8(@Nullable String text)
throws BufferOverflowException, IllegalStateException {
if (text == null) {
BytesInternal.writeStopBitNeg1(this);
return this;
}
if (Jvm.isJava9Plus()) {
byte[] strBytes = extractBytes(text);
byte coder = StringUtils.getStringCoder(text);
long utfLength = AppendableUtil.findUtf8Length(strBytes, coder);
writeStopBit(utfLength);
appendUtf8(strBytes, 0, text.length(), coder);
} else {
char[] chars = extractChars(text);
long utfLength = AppendableUtil.findUtf8Length(chars);
writeStopBit(utfLength);
if (utfLength == chars.length)
append8bit(chars);
else
appendUtf8(chars, 0, chars.length);
}
return this;
}
void append8bit(@NotNull char[] chars)
throws BufferOverflowException, IllegalArgumentException, IllegalStateException {
requireNonNull(chars);
long wp = writePosition();
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
bytesStore.writeByte(wp++, (byte) c);
}
uncheckedWritePosition(wp);
}
@NotNull
@Override
public Bytes appendUtf8(char[] chars, @NonNegative int offset, @NonNegative int length)
throws BufferOverflowException, IllegalArgumentException, IllegalStateException {
requireNonNull(chars);
long wp = writePosition();
int i;
ascii:
{
for (i = 0; i < length; i++) {
char c = chars[offset + i];
if (c > 0x007F)
break ascii;
bytesStore.writeByte(wp++, (byte) c);
}
uncheckedWritePosition(wp);
return this;
}
for (; i < length; i++) {
char c = chars[offset + i];
BytesInternal.appendUtf8Char(this, c);
}
uncheckedWritePosition(wp);
return this;
}
}