7775460984
This supports more use cases. The ensureWritable method can now amortise its allocation cost by allocating more than what is strictly necessary to satisfy the immediate call. The bifurcate method can now split at a given offset.
1572 lines
48 KiB
Java
1572 lines
48 KiB
Java
/*
|
|
* Copyright 2021 The Netty Project
|
|
*
|
|
* The Netty Project 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:
|
|
*
|
|
* https://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 io.netty.buffer.api.unsafe;
|
|
|
|
import io.netty.buffer.api.AllocatorControl;
|
|
import io.netty.buffer.api.Buffer;
|
|
import io.netty.buffer.api.BufferAllocator;
|
|
import io.netty.buffer.api.ByteCursor;
|
|
import io.netty.buffer.api.Drop;
|
|
import io.netty.buffer.api.Owned;
|
|
import io.netty.buffer.api.RcSupport;
|
|
import io.netty.buffer.api.ReadableComponent;
|
|
import io.netty.buffer.api.ReadableComponentProcessor;
|
|
import io.netty.buffer.api.WritableComponent;
|
|
import io.netty.buffer.api.WritableComponentProcessor;
|
|
import io.netty.buffer.api.internal.ArcDrop;
|
|
import io.netty.buffer.api.internal.Statics;
|
|
import io.netty.util.internal.PlatformDependent;
|
|
|
|
import java.lang.ref.Reference;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.ByteOrder;
|
|
|
|
import static io.netty.buffer.api.internal.Statics.bufferIsClosed;
|
|
import static io.netty.buffer.api.internal.Statics.bufferIsReadOnly;
|
|
import static io.netty.util.internal.PlatformDependent.BIG_ENDIAN_NATIVE_ORDER;
|
|
|
|
public class UnsafeBuffer extends RcSupport<Buffer, UnsafeBuffer> implements Buffer, ReadableComponent,
|
|
WritableComponent {
|
|
private static final int CLOSED_SIZE = -1;
|
|
private static final boolean ACCESS_UNALIGNED = PlatformDependent.isUnaligned();
|
|
private UnsafeMemory memory; // The memory liveness; monitored by Cleaner.
|
|
private Object base; // On-heap address reference object, or null for off-heap.
|
|
private long baseOffset; // Offset of this buffer into the memory.
|
|
private long address; // Resolved address (baseOffset + memory.address).
|
|
private int rsize;
|
|
private int wsize;
|
|
private final AllocatorControl control;
|
|
private ByteOrder order;
|
|
private boolean flipBytes;
|
|
private boolean readOnly;
|
|
private int roff;
|
|
private int woff;
|
|
|
|
public UnsafeBuffer(UnsafeMemory memory, long offset, int size, AllocatorControl allocatorControl,
|
|
Drop<UnsafeBuffer> drop) {
|
|
super(new MakeInaccisbleOnDrop(ArcDrop.wrap(drop)));
|
|
this.memory = memory;
|
|
base = memory.base;
|
|
baseOffset = offset;
|
|
address = memory.address + offset;
|
|
rsize = size;
|
|
wsize = size;
|
|
control = allocatorControl;
|
|
order = ByteOrder.nativeOrder();
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "Buffer[roff:" + roff + ", woff:" + woff + ", cap:" + rsize + ']';
|
|
}
|
|
|
|
@Override
|
|
public Buffer order(ByteOrder order) {
|
|
this.order = order;
|
|
flipBytes = order != ByteOrder.nativeOrder();
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public ByteOrder order() {
|
|
return order;
|
|
}
|
|
|
|
@Override
|
|
public int capacity() {
|
|
return Math.max(0, rsize); // Use Math.max to make capacity of closed buffer equal to zero.
|
|
}
|
|
|
|
@Override
|
|
public int readerOffset() {
|
|
return roff;
|
|
}
|
|
|
|
@Override
|
|
public Buffer readerOffset(int offset) {
|
|
checkRead(offset, 0);
|
|
roff = offset;
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public int writerOffset() {
|
|
return woff;
|
|
}
|
|
|
|
@Override
|
|
public Buffer writerOffset(int offset) {
|
|
checkWrite(offset, 0);
|
|
woff = offset;
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer fill(byte value) {
|
|
checkSet(0, capacity());
|
|
if (rsize == CLOSED_SIZE) {
|
|
throw bufferIsClosed();
|
|
}
|
|
try {
|
|
PlatformDependent.setMemory(base, address, rsize, value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public long nativeAddress() {
|
|
return base == null? address : 0;
|
|
}
|
|
|
|
@Override
|
|
public Buffer readOnly(boolean readOnly) {
|
|
this.readOnly = readOnly;
|
|
wsize = readOnly? CLOSED_SIZE : rsize;
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public boolean readOnly() {
|
|
return readOnly;
|
|
}
|
|
|
|
@Override
|
|
public Buffer slice(int offset, int length) {
|
|
if (length < 0) {
|
|
throw new IllegalArgumentException("Length cannot be negative: " + length + '.');
|
|
}
|
|
checkGet(offset, length);
|
|
ArcDrop<UnsafeBuffer> drop = (ArcDrop<UnsafeBuffer>) unsafeGetDrop();
|
|
drop.increment();
|
|
return new UnsafeBuffer(memory, baseOffset + offset, length, control, drop)
|
|
.writerOffset(length)
|
|
.order(order)
|
|
.readOnly(readOnly);
|
|
}
|
|
|
|
@Override
|
|
public void copyInto(int srcPos, byte[] dest, int destPos, int length) {
|
|
checkCopyIntoArgs(srcPos, length, destPos, dest.length);
|
|
copyIntoArray(srcPos, dest, destPos, length);
|
|
}
|
|
|
|
private void copyIntoArray(int srcPos, byte[] dest, int destPos, int length) {
|
|
long destOffset = PlatformDependent.byteArrayBaseOffset();
|
|
try {
|
|
PlatformDependent.copyMemory(base, address + srcPos, dest, destOffset + destPos, length);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
Reference.reachabilityFence(dest);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void copyInto(int srcPos, ByteBuffer dest, int destPos, int length) {
|
|
checkCopyIntoArgs(srcPos, length, destPos, dest.capacity());
|
|
if (dest.hasArray()) {
|
|
copyIntoArray(srcPos, dest.array(), dest.arrayOffset() + destPos, length);
|
|
} else {
|
|
assert dest.isDirect();
|
|
long destAddr = PlatformDependent.directBufferAddress(dest);
|
|
try {
|
|
PlatformDependent.copyMemory(base, address + srcPos, null, destAddr + destPos, length);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
Reference.reachabilityFence(dest);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void checkCopyIntoArgs(int srcPos, int length, int destPos, int destLength) {
|
|
if (rsize == CLOSED_SIZE) {
|
|
throw bufferIsClosed();
|
|
}
|
|
if (srcPos < 0) {
|
|
throw new IllegalArgumentException("The srcPos cannot be negative: " + srcPos + '.');
|
|
}
|
|
if (length < 0) {
|
|
throw new IllegalArgumentException("The length cannot be negative: " + length + '.');
|
|
}
|
|
if (rsize < srcPos + length) {
|
|
throw new IllegalArgumentException("The srcPos + length is beyond the end of the buffer: " +
|
|
"srcPos = " + srcPos + ", length = " + length + '.');
|
|
}
|
|
if (destPos < 0) {
|
|
throw new IllegalArgumentException("The destPos cannot be negative: " + destPos + '.');
|
|
}
|
|
if (destLength < destPos + length) {
|
|
throw new IllegalArgumentException("The destPos + length is beyond the end of the destination: " +
|
|
"destPos = " + destPos + ", length = " + length + '.');
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void copyInto(int srcPos, Buffer dest, int destPos, int length) {
|
|
checkCopyIntoArgs(srcPos, length, destPos, dest.capacity());
|
|
if (dest.readOnly()) {
|
|
throw bufferIsReadOnly();
|
|
}
|
|
long nativeAddress = dest.nativeAddress();
|
|
try {
|
|
if (nativeAddress != 0) {
|
|
PlatformDependent.copyMemory(base, address + srcPos, null, nativeAddress + destPos, length);
|
|
} else if (dest instanceof UnsafeBuffer) {
|
|
UnsafeBuffer destUnsafe = (UnsafeBuffer) dest;
|
|
PlatformDependent.copyMemory(
|
|
base, address + srcPos, destUnsafe.base, destUnsafe.address + destPos, length);
|
|
} else {
|
|
Statics.copyToViaReverseCursor(this, srcPos, dest, destPos, length);
|
|
}
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
Reference.reachabilityFence(dest);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public ByteCursor openCursor() {
|
|
return openCursor(readerOffset(), readableBytes());
|
|
}
|
|
|
|
@Override
|
|
public ByteCursor openCursor(int fromOffset, int length) {
|
|
if (rsize == CLOSED_SIZE) {
|
|
throw bufferIsClosed();
|
|
}
|
|
if (fromOffset < 0) {
|
|
throw new IllegalArgumentException("The fromOffset cannot be negative: " + fromOffset + '.');
|
|
}
|
|
if (length < 0) {
|
|
throw new IllegalArgumentException("The length cannot be negative: " + length + '.');
|
|
}
|
|
if (capacity() < fromOffset + length) {
|
|
throw new IllegalArgumentException("The fromOffset + length is beyond the end of the buffer: " +
|
|
"fromOffset = " + fromOffset + ", length = " + length + '.');
|
|
}
|
|
return new ByteCursor() {
|
|
final UnsafeMemory memory = UnsafeBuffer.this.memory; // Keep memory alive.
|
|
final Object baseObj = base;
|
|
final long baseAddress = address;
|
|
int index = fromOffset;
|
|
final int end = index + length;
|
|
long longValue = -1;
|
|
byte byteValue = -1;
|
|
|
|
@Override
|
|
public boolean readLong() {
|
|
if (index + Long.BYTES <= end) {
|
|
try {
|
|
long value = PlatformDependent.getLong(baseObj, baseAddress + index);
|
|
longValue = BIG_ENDIAN_NATIVE_ORDER? value : Long.reverseBytes(value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
index += Long.BYTES;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public long getLong() {
|
|
return longValue;
|
|
}
|
|
|
|
@Override
|
|
public boolean readByte() {
|
|
if (index < end) {
|
|
try {
|
|
byteValue = PlatformDependent.getByte(baseObj, baseAddress + index);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
index++;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public byte getByte() {
|
|
return byteValue;
|
|
}
|
|
|
|
@Override
|
|
public int currentOffset() {
|
|
return index;
|
|
}
|
|
|
|
@Override
|
|
public int bytesLeft() {
|
|
return end - index;
|
|
}
|
|
};
|
|
}
|
|
|
|
@Override
|
|
public ByteCursor openReverseCursor(int fromOffset, int length) {
|
|
if (rsize == CLOSED_SIZE) {
|
|
throw bufferIsClosed();
|
|
}
|
|
if (fromOffset < 0) {
|
|
throw new IllegalArgumentException("The fromOffset cannot be negative: " + fromOffset + '.');
|
|
}
|
|
if (length < 0) {
|
|
throw new IllegalArgumentException("The length cannot be negative: " + length + '.');
|
|
}
|
|
if (capacity() <= fromOffset) {
|
|
throw new IllegalArgumentException("The fromOffset is beyond the end of the buffer: " + fromOffset + '.');
|
|
}
|
|
if (fromOffset - length < -1) {
|
|
throw new IllegalArgumentException("The fromOffset - length would underflow the buffer: " +
|
|
"fromOffset = " + fromOffset + ", length = " + length + '.');
|
|
}
|
|
return new ByteCursor() {
|
|
final UnsafeMemory memory = UnsafeBuffer.this.memory; // Keep memory alive.
|
|
final Object baseObj = base;
|
|
final long baseAddress = address;
|
|
int index = fromOffset;
|
|
final int end = index - length;
|
|
long longValue = -1;
|
|
byte byteValue = -1;
|
|
|
|
@Override
|
|
public boolean readLong() {
|
|
if (index - Long.BYTES >= end) {
|
|
index -= 7;
|
|
try {
|
|
long value = PlatformDependent.getLong(baseObj, baseAddress + index);
|
|
longValue = BIG_ENDIAN_NATIVE_ORDER? Long.reverseBytes(value) : value;
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
index--;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public long getLong() {
|
|
return longValue;
|
|
}
|
|
|
|
@Override
|
|
public boolean readByte() {
|
|
if (index > end) {
|
|
try {
|
|
byteValue = PlatformDependent.getByte(baseObj, baseAddress + index);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
index--;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public byte getByte() {
|
|
return byteValue;
|
|
}
|
|
|
|
@Override
|
|
public int currentOffset() {
|
|
return index;
|
|
}
|
|
|
|
@Override
|
|
public int bytesLeft() {
|
|
return index - end;
|
|
}
|
|
};
|
|
}
|
|
|
|
@Override
|
|
public void ensureWritable(int size, int minimumGrowth, boolean allowCompaction) {
|
|
if (!isOwned()) {
|
|
throw attachTrace(new IllegalStateException(
|
|
"Buffer is not owned. Only owned buffers can call ensureWritable."));
|
|
}
|
|
if (size < 0) {
|
|
throw new IllegalArgumentException("Cannot ensure writable for a negative size: " + size + '.');
|
|
}
|
|
if (minimumGrowth < 0) {
|
|
throw new IllegalArgumentException("The minimum growth cannot be negative: " + minimumGrowth + '.');
|
|
}
|
|
if (rsize != wsize) {
|
|
throw bufferIsReadOnly();
|
|
}
|
|
if (writableBytes() >= size) {
|
|
// We already have enough space.
|
|
return;
|
|
}
|
|
|
|
if (allowCompaction && writableBytes() + readerOffset() >= size) {
|
|
// We can solve this with compaction.
|
|
compact();
|
|
return;
|
|
}
|
|
|
|
// Allocate a bigger buffer.
|
|
long newSize = capacity() + (long) Math.max(size - writableBytes(), minimumGrowth);
|
|
BufferAllocator.checkSize(newSize);
|
|
UnsafeMemory memory = (UnsafeMemory) control.allocateUntethered(this, (int) newSize);
|
|
|
|
// Copy contents.
|
|
try {
|
|
PlatformDependent.copyMemory(base, address, memory.base, memory.address, rsize);
|
|
} finally {
|
|
Reference.reachabilityFence(this.memory);
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
|
|
// Release old memory:
|
|
var drop = (Drop<UnsafeBuffer>) unsafeGetDrop();
|
|
int roff = this.roff;
|
|
int woff = this.woff;
|
|
drop.drop(this);
|
|
while (drop instanceof ArcDrop) {
|
|
drop = ((ArcDrop<UnsafeBuffer>) drop).unwrap();
|
|
}
|
|
unsafeSetDrop(new ArcDrop<>(drop));
|
|
this.roff = roff;
|
|
this.woff = woff;
|
|
|
|
this.memory = memory;
|
|
base = memory.base;
|
|
baseOffset = 0;
|
|
address = memory.address;
|
|
rsize = memory.size;
|
|
wsize = memory.size;
|
|
drop.attach(this);
|
|
}
|
|
|
|
@Override
|
|
public Buffer bifurcate(int splitOffset) {
|
|
if (splitOffset < 0) {
|
|
throw new IllegalArgumentException("The split offset cannot be negative: " + splitOffset + '.');
|
|
}
|
|
if (capacity() < splitOffset) {
|
|
throw new IllegalArgumentException("The split offset cannot be greater than the buffer capacity, " +
|
|
"but the split offset was " + splitOffset + ", and capacity is " + capacity() + '.');
|
|
}
|
|
if (!isOwned()) {
|
|
throw attachTrace(new IllegalStateException("Cannot bifurcate a buffer that is not owned."));
|
|
}
|
|
var drop = (ArcDrop<UnsafeBuffer>) unsafeGetDrop();
|
|
unsafeSetDrop(new ArcDrop<>(drop));
|
|
// TODO maybe incrementing the existing ArcDrop is enough; maybe we don't need to wrap it in another ArcDrop.
|
|
var bifurcatedBuf = new UnsafeBuffer(memory, baseOffset, splitOffset, control, new ArcDrop<>(drop.increment()));
|
|
bifurcatedBuf.woff = Math.min(woff, splitOffset);
|
|
bifurcatedBuf.roff = Math.min(roff, splitOffset);
|
|
bifurcatedBuf.order(order());
|
|
boolean readOnly = readOnly();
|
|
bifurcatedBuf.readOnly(readOnly);
|
|
rsize -= splitOffset;
|
|
baseOffset += splitOffset;
|
|
address += splitOffset;
|
|
if (!readOnly) {
|
|
wsize = rsize;
|
|
}
|
|
woff = Math.max(woff, splitOffset) - splitOffset;
|
|
roff = Math.max(roff, splitOffset) - splitOffset;
|
|
return bifurcatedBuf;
|
|
}
|
|
|
|
@Override
|
|
public void compact() {
|
|
if (!isOwned()) {
|
|
throw attachTrace(new IllegalStateException("Buffer must be owned in order to compact."));
|
|
}
|
|
if (readOnly()) {
|
|
throw new IllegalStateException("Buffer must be writable in order to compact, but was read-only.");
|
|
}
|
|
if (roff == 0) {
|
|
return;
|
|
}
|
|
try {
|
|
PlatformDependent.copyMemory(base, address + roff, base, address, woff - roff);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
woff -= roff;
|
|
roff = 0;
|
|
}
|
|
|
|
@Override
|
|
public int countComponents() {
|
|
return 1;
|
|
}
|
|
|
|
@Override
|
|
public int countReadableComponents() {
|
|
return readableBytes() > 0? 1 : 0;
|
|
}
|
|
|
|
@Override
|
|
public int countWritableComponents() {
|
|
return writableBytes() > 0? 1 : 0;
|
|
}
|
|
|
|
// <editor-fold defaultstate="collapsed" desc="Readable/WritableComponent implementation.">
|
|
@Override
|
|
public boolean hasReadableArray() {
|
|
return base instanceof byte[];
|
|
}
|
|
|
|
@Override
|
|
public byte[] readableArray() {
|
|
checkHasReadableArray();
|
|
return (byte[]) base;
|
|
}
|
|
|
|
@Override
|
|
public int readableArrayOffset() {
|
|
checkHasReadableArray();
|
|
return Math.toIntExact(address + roff - PlatformDependent.byteArrayBaseOffset());
|
|
}
|
|
|
|
private void checkHasReadableArray() {
|
|
if (!hasReadableArray()) {
|
|
throw new UnsupportedOperationException("No readable array available.");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int readableArrayLength() {
|
|
return woff - roff;
|
|
}
|
|
|
|
@Override
|
|
public long readableNativeAddress() {
|
|
return nativeAddress();
|
|
}
|
|
|
|
@Override
|
|
public ByteBuffer readableBuffer() {
|
|
final ByteBuffer buf;
|
|
if (hasReadableArray()) {
|
|
buf = ByteBuffer.wrap(readableArray()).slice(readableArrayOffset(), readableArrayLength());
|
|
} else {
|
|
buf = PlatformDependent.directBuffer(address + roff, readableBytes());
|
|
}
|
|
return buf.asReadOnlyBuffer().order(order());
|
|
}
|
|
|
|
@Override
|
|
public boolean hasWritableArray() {
|
|
return hasReadableArray();
|
|
}
|
|
|
|
@Override
|
|
public byte[] writableArray() {
|
|
checkHasWritableArray();
|
|
return (byte[]) base;
|
|
}
|
|
|
|
@Override
|
|
public int writableArrayOffset() {
|
|
checkHasWritableArray();
|
|
return Math.toIntExact(address + woff - PlatformDependent.byteArrayBaseOffset());
|
|
}
|
|
|
|
private void checkHasWritableArray() {
|
|
if (!hasReadableArray()) {
|
|
throw new UnsupportedOperationException("No writable array available.");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int writableArrayLength() {
|
|
return capacity() - woff;
|
|
}
|
|
|
|
@Override
|
|
public long writableNativeAddress() {
|
|
return nativeAddress();
|
|
}
|
|
|
|
@Override
|
|
public ByteBuffer writableBuffer() {
|
|
final ByteBuffer buf;
|
|
if (hasWritableArray()) {
|
|
buf = ByteBuffer.wrap(writableArray()).slice(writableArrayOffset(), writableArrayLength());
|
|
} else {
|
|
buf = PlatformDependent.directBuffer(address + woff, writableBytes());
|
|
}
|
|
return buf.order(order());
|
|
}
|
|
// </editor-fold>
|
|
|
|
@Override
|
|
public <E extends Exception> int forEachReadable(int initialIndex, ReadableComponentProcessor<E> processor)
|
|
throws E {
|
|
checkRead(readerOffset(), Math.max(1, readableBytes()));
|
|
return processor.process(initialIndex, this)? 1 : -1;
|
|
}
|
|
|
|
@Override
|
|
public <E extends Exception> int forEachWritable(int initialIndex, WritableComponentProcessor<E> processor)
|
|
throws E {
|
|
checkWrite(writerOffset(), Math.max(1, writableBytes()));
|
|
return processor.process(initialIndex, this)? 1 : -1;
|
|
}
|
|
|
|
@Override
|
|
public byte readByte() {
|
|
checkRead(roff, Byte.BYTES);
|
|
try {
|
|
var value = loadByte(address + roff);
|
|
roff += Byte.BYTES;
|
|
return value;
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public byte getByte(int roff) {
|
|
checkGet(roff, Byte.BYTES);
|
|
try {
|
|
return loadByte(address + roff);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int readUnsignedByte() {
|
|
return readByte() & 0xFF;
|
|
}
|
|
|
|
@Override
|
|
public int getUnsignedByte(int roff) {
|
|
return getByte(roff) & 0xFF;
|
|
}
|
|
|
|
@Override
|
|
public Buffer writeByte(byte value) {
|
|
checkWrite(woff, Byte.BYTES);
|
|
long offset = address + woff;
|
|
woff += Byte.BYTES;
|
|
try {
|
|
storeByte(offset, value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer setByte(int woff, byte value) {
|
|
checkSet(woff, Byte.BYTES);
|
|
long offset = address + woff;
|
|
try {
|
|
storeByte(offset, value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer writeUnsignedByte(int value) {
|
|
checkWrite(woff, Byte.BYTES);
|
|
long offset = address + woff;
|
|
woff += Byte.BYTES;
|
|
try {
|
|
storeByte(offset, (byte) (value & 0xFF));
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer setUnsignedByte(int woff, int value) {
|
|
checkSet(woff, Byte.BYTES);
|
|
long offset = address + woff;
|
|
try {
|
|
storeByte(offset, (byte) (value & 0xFF));
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public char readChar() {
|
|
checkRead(roff, Character.BYTES);
|
|
try {
|
|
long offset = address + roff;
|
|
roff += Character.BYTES;
|
|
return loadChar(offset);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public char getChar(int roff) {
|
|
checkGet(roff, Character.BYTES);
|
|
try {
|
|
long offset = address + roff;
|
|
return loadChar(offset);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Buffer writeChar(char value) {
|
|
checkWrite(woff, Character.BYTES);
|
|
long offset = address + woff;
|
|
woff += Character.BYTES;
|
|
try {
|
|
storeChar(offset, value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer setChar(int woff, char value) {
|
|
checkSet(woff, Character.BYTES);
|
|
long offset = address + woff;
|
|
try {
|
|
storeChar(offset, value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public short readShort() {
|
|
checkRead(roff, Short.BYTES);
|
|
try {
|
|
long offset = address + roff;
|
|
roff += Short.BYTES;
|
|
return loadShort(offset);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public short getShort(int roff) {
|
|
checkGet(roff, Short.BYTES);
|
|
try {
|
|
long offset = address + roff;
|
|
return loadShort(offset);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int readUnsignedShort() {
|
|
return readShort() & 0xFFFF;
|
|
}
|
|
|
|
@Override
|
|
public int getUnsignedShort(int roff) {
|
|
return getShort(roff) & 0xFFFF;
|
|
}
|
|
|
|
@Override
|
|
public Buffer writeShort(short value) {
|
|
checkWrite(woff, Short.BYTES);
|
|
long offset = address + woff;
|
|
woff += Short.BYTES;
|
|
try {
|
|
storeShort(offset, value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer setShort(int woff, short value) {
|
|
checkSet(woff, Short.BYTES);
|
|
long offset = address + woff;
|
|
try {
|
|
storeShort(offset, value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer writeUnsignedShort(int value) {
|
|
checkWrite(woff, Short.BYTES);
|
|
long offset = address + woff;
|
|
woff += Short.BYTES;
|
|
try {
|
|
storeShort(offset, (short) (value & 0xFFFF));
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer setUnsignedShort(int woff, int value) {
|
|
checkSet(woff, Short.BYTES);
|
|
long offset = address + woff;
|
|
try {
|
|
storeShort(offset, (short) (value & 0xFFFF));
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public int readMedium() {
|
|
checkRead(roff, 3);
|
|
long offset = address + roff;
|
|
int value = order() == ByteOrder.BIG_ENDIAN ?
|
|
loadByte(offset) << 16 |
|
|
(loadByte(offset + 1) & 0xFF) << 8 |
|
|
loadByte(offset + 2) & 0xFF :
|
|
loadByte(offset) & 0xFF |
|
|
(loadByte(offset + 1) & 0xFF) << 8 |
|
|
loadByte(offset + 2) << 16;
|
|
roff += 3;
|
|
return value;
|
|
}
|
|
|
|
@Override
|
|
public int getMedium(int roff) {
|
|
checkGet(roff, 3);
|
|
long offset = address + roff;
|
|
return order() == ByteOrder.BIG_ENDIAN?
|
|
loadByte(offset) << 16 |
|
|
(loadByte(offset + 1) & 0xFF) << 8 |
|
|
loadByte(offset + 2) & 0xFF :
|
|
loadByte(offset) & 0xFF |
|
|
(loadByte(offset + 1) & 0xFF) << 8 |
|
|
loadByte(offset + 2) << 16;
|
|
}
|
|
|
|
@Override
|
|
public int readUnsignedMedium() {
|
|
checkRead(roff, 3);
|
|
long offset = address + roff;
|
|
int value = order() == ByteOrder.BIG_ENDIAN?
|
|
(loadByte(offset) << 16 |
|
|
(loadByte(offset + 1) & 0xFF) << 8 |
|
|
loadByte(offset + 2) & 0xFF) & 0xFFFFFF :
|
|
(loadByte(offset) & 0xFF |
|
|
(loadByte(offset + 1) & 0xFF) << 8 |
|
|
loadByte(offset + 2) << 16) & 0xFFFFFF;
|
|
roff += 3;
|
|
return value;
|
|
}
|
|
|
|
@Override
|
|
public int getUnsignedMedium(int roff) {
|
|
checkGet(roff, 3);
|
|
long offset = address + roff;
|
|
return order() == ByteOrder.BIG_ENDIAN?
|
|
(loadByte(offset) << 16 |
|
|
(loadByte(offset + 1) & 0xFF) << 8 |
|
|
loadByte(offset + 2) & 0xFF) & 0xFFFFFF :
|
|
(loadByte(offset) & 0xFF |
|
|
(loadByte(offset + 1) & 0xFF) << 8 |
|
|
loadByte(offset + 2) << 16) & 0xFFFFFF;
|
|
}
|
|
|
|
@Override
|
|
public Buffer writeMedium(int value) {
|
|
checkWrite(woff, 3);
|
|
long offset = address + woff;
|
|
if (order() == ByteOrder.BIG_ENDIAN) {
|
|
storeByte(offset, (byte) (value >> 16));
|
|
storeByte(offset + 1, (byte) (value >> 8 & 0xFF));
|
|
storeByte(offset + 2, (byte) (value & 0xFF));
|
|
} else {
|
|
storeByte(offset, (byte) (value & 0xFF));
|
|
storeByte(offset + 1, (byte) (value >> 8 & 0xFF));
|
|
storeByte(offset + 2, (byte) (value >> 16 & 0xFF));
|
|
}
|
|
woff += 3;
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer setMedium(int woff, int value) {
|
|
checkSet(woff, 3);
|
|
long offset = address + woff;
|
|
if (order() == ByteOrder.BIG_ENDIAN) {
|
|
storeByte(offset, (byte) (value >> 16));
|
|
storeByte(offset + 1, (byte) (value >> 8 & 0xFF));
|
|
storeByte(offset + 2, (byte) (value & 0xFF));
|
|
} else {
|
|
storeByte(offset, (byte) (value & 0xFF));
|
|
storeByte(offset + 1, (byte) (value >> 8 & 0xFF));
|
|
storeByte(offset + 2, (byte) (value >> 16 & 0xFF));
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer writeUnsignedMedium(int value) {
|
|
checkWrite(woff, 3);
|
|
long offset = address + woff;
|
|
if (order() == ByteOrder.BIG_ENDIAN) {
|
|
storeByte(offset, (byte) (value >> 16));
|
|
storeByte(offset + 1, (byte) (value >> 8 & 0xFF));
|
|
storeByte(offset + 2, (byte) (value & 0xFF));
|
|
} else {
|
|
storeByte(offset, (byte) (value & 0xFF));
|
|
storeByte(offset + 1, (byte) (value >> 8 & 0xFF));
|
|
storeByte(offset + 2, (byte) (value >> 16 & 0xFF));
|
|
}
|
|
woff += 3;
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer setUnsignedMedium(int woff, int value) {
|
|
checkSet(woff, 3);
|
|
long offset = address + woff;
|
|
if (order() == ByteOrder.BIG_ENDIAN) {
|
|
storeByte(offset, (byte) (value >> 16));
|
|
storeByte(offset + 1, (byte) (value >> 8 & 0xFF));
|
|
storeByte(offset + 2, (byte) (value & 0xFF));
|
|
} else {
|
|
storeByte(offset, (byte) (value & 0xFF));
|
|
storeByte(offset + 1, (byte) (value >> 8 & 0xFF));
|
|
storeByte(offset + 2, (byte) (value >> 16 & 0xFF));
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public int readInt() {
|
|
checkRead(roff, Integer.BYTES);
|
|
try {
|
|
long offset = address + roff;
|
|
roff += Integer.BYTES;
|
|
return loadInt(offset);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getInt(int roff) {
|
|
checkGet(roff, Integer.BYTES);
|
|
try {
|
|
long offset = address + roff;
|
|
return loadInt(offset);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public long readUnsignedInt() {
|
|
return readInt() & 0x0000_0000_FFFF_FFFFL;
|
|
}
|
|
|
|
@Override
|
|
public long getUnsignedInt(int roff) {
|
|
return getInt(roff) & 0x0000_0000_FFFF_FFFFL;
|
|
}
|
|
|
|
@Override
|
|
public Buffer writeInt(int value) {
|
|
checkWrite(woff, Integer.BYTES);
|
|
long offset = address + woff;
|
|
woff += Integer.BYTES;
|
|
try {
|
|
storeInt(offset, value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer setInt(int woff, int value) {
|
|
checkSet(woff, Integer.BYTES);
|
|
long offset = address + woff;
|
|
try {
|
|
storeInt(offset, value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer writeUnsignedInt(long value) {
|
|
checkWrite(woff, Integer.BYTES);
|
|
long offset = address + woff;
|
|
woff += Integer.BYTES;
|
|
try {
|
|
storeInt(offset, (int) (value & 0xFFFF_FFFFL));
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer setUnsignedInt(int woff, long value) {
|
|
checkSet(woff, Integer.BYTES);
|
|
long offset = address + woff;
|
|
try {
|
|
storeInt(offset, (int) (value & 0xFFFF_FFFFL));
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public float readFloat() {
|
|
checkRead(roff, Float.BYTES);
|
|
try {
|
|
long offset = address + roff;
|
|
roff += Float.BYTES;
|
|
return loadFloat(offset);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public float getFloat(int roff) {
|
|
checkGet(roff, Float.BYTES);
|
|
try {
|
|
long offset = address + roff;
|
|
return loadFloat(offset);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Buffer writeFloat(float value) {
|
|
checkWrite(woff, Float.BYTES);
|
|
long offset = address + woff;
|
|
woff += Float.BYTES;
|
|
try {
|
|
storeFloat(offset, value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer setFloat(int woff, float value) {
|
|
checkSet(woff, Float.BYTES);
|
|
long offset = address + woff;
|
|
try {
|
|
storeFloat(offset, value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public long readLong() {
|
|
checkRead(roff, Long.BYTES);
|
|
try {
|
|
long offset = address + roff;
|
|
roff += Long.BYTES;
|
|
return loadLong(offset);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public long getLong(int roff) {
|
|
checkGet(roff, Long.BYTES);
|
|
try {
|
|
long offset = address + roff;
|
|
return loadLong(offset);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Buffer writeLong(long value) {
|
|
checkWrite(woff, Long.BYTES);
|
|
long offset = address + woff;
|
|
woff += Long.BYTES;
|
|
try {
|
|
storeLong(offset, value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer setLong(int woff, long value) {
|
|
checkSet(woff, Long.BYTES);
|
|
long offset = address + woff;
|
|
try {
|
|
storeLong(offset, value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public double readDouble() {
|
|
checkRead(roff, Double.BYTES);
|
|
try {
|
|
long offset = address + roff;
|
|
roff += Double.BYTES;
|
|
return loadDouble(offset);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public double getDouble(int roff) {
|
|
checkGet(roff, Double.BYTES);
|
|
try {
|
|
long offset = address + roff;
|
|
return loadDouble(offset);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Buffer writeDouble(double value) {
|
|
checkWrite(woff, Double.BYTES);
|
|
long offset = address + woff;
|
|
woff += Double.BYTES;
|
|
try {
|
|
storeDouble(offset, value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public Buffer setDouble(int woff, double value) {
|
|
checkSet(woff, Double.BYTES);
|
|
long offset = address + woff;
|
|
try {
|
|
storeDouble(offset, value);
|
|
} finally {
|
|
Reference.reachabilityFence(memory);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
protected Owned<UnsafeBuffer> prepareSend() {
|
|
var order = order();
|
|
var roff = this.roff;
|
|
var woff = this.woff;
|
|
var readOnly = readOnly();
|
|
UnsafeMemory memory = this.memory;
|
|
AllocatorControl control = this.control;
|
|
long baseOffset = this.baseOffset;
|
|
int rsize = this.rsize;
|
|
makeInaccessible();
|
|
return new Owned<UnsafeBuffer>() {
|
|
@Override
|
|
public UnsafeBuffer transferOwnership(Drop<UnsafeBuffer> drop) {
|
|
UnsafeBuffer copy = new UnsafeBuffer(memory, baseOffset, rsize, control, drop);
|
|
copy.order(order);
|
|
copy.roff = roff;
|
|
copy.woff = woff;
|
|
copy.readOnly(readOnly);
|
|
return copy;
|
|
}
|
|
};
|
|
}
|
|
|
|
@Override
|
|
protected Drop<UnsafeBuffer> unsafeGetDrop() {
|
|
MakeInaccisbleOnDrop drop = (MakeInaccisbleOnDrop) super.unsafeGetDrop();
|
|
return drop.delegate;
|
|
}
|
|
|
|
@Override
|
|
protected void unsafeSetDrop(Drop<UnsafeBuffer> replacement) {
|
|
super.unsafeSetDrop(new MakeInaccisbleOnDrop(replacement));
|
|
}
|
|
|
|
private static final class MakeInaccisbleOnDrop implements Drop<UnsafeBuffer> {
|
|
final Drop<UnsafeBuffer> delegate;
|
|
|
|
private MakeInaccisbleOnDrop(Drop<UnsafeBuffer> delegate) {
|
|
this.delegate = delegate;
|
|
}
|
|
|
|
@Override
|
|
public void drop(UnsafeBuffer buf) {
|
|
try {
|
|
delegate.drop(buf);
|
|
} finally {
|
|
buf.makeInaccessible();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void attach(UnsafeBuffer buf) {
|
|
delegate.attach(buf);
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "UnsafeDrop(" + delegate + ')';
|
|
}
|
|
}
|
|
|
|
void makeInaccessible() {
|
|
roff = 0;
|
|
woff = 0;
|
|
rsize = CLOSED_SIZE;
|
|
wsize = CLOSED_SIZE;
|
|
readOnly = false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isOwned() {
|
|
return super.isOwned() && ((ArcDrop<UnsafeBuffer>) unsafeGetDrop()).isOwned();
|
|
}
|
|
|
|
@Override
|
|
public int countBorrows() {
|
|
return super.countBorrows() + ((ArcDrop<UnsafeBuffer>) unsafeGetDrop()).countBorrows();
|
|
}
|
|
|
|
private void checkRead(int index, int size) {
|
|
if (index < 0 || woff < index + size) {
|
|
throw readAccessCheckException(index);
|
|
}
|
|
}
|
|
|
|
private void checkGet(int index, int size) {
|
|
if (index < 0 || rsize < index + size) {
|
|
throw readAccessCheckException(index);
|
|
}
|
|
}
|
|
|
|
private void checkWrite(int index, int size) {
|
|
if (index < roff || wsize < index + size) {
|
|
throw writeAccessCheckException(index);
|
|
}
|
|
}
|
|
|
|
private void checkSet(int index, int size) {
|
|
if (index < 0 || wsize < index + size) {
|
|
throw writeAccessCheckException(index);
|
|
}
|
|
}
|
|
|
|
private RuntimeException readAccessCheckException(int index) {
|
|
if (rsize == CLOSED_SIZE) {
|
|
throw bufferIsClosed();
|
|
}
|
|
return outOfBounds(index);
|
|
}
|
|
|
|
private RuntimeException writeAccessCheckException(int index) {
|
|
if (rsize == CLOSED_SIZE) {
|
|
throw bufferIsClosed();
|
|
}
|
|
if (wsize != rsize) {
|
|
return bufferIsReadOnly();
|
|
}
|
|
return outOfBounds(index);
|
|
}
|
|
|
|
private IndexOutOfBoundsException outOfBounds(int index) {
|
|
return new IndexOutOfBoundsException(
|
|
"Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " +
|
|
rsize + "].");
|
|
}
|
|
|
|
private byte loadByte(long off) {
|
|
return PlatformDependent.getByte(base, off);
|
|
}
|
|
|
|
private char loadChar(long offset) {
|
|
if (ACCESS_UNALIGNED) {
|
|
var value = PlatformDependent.getChar(base, offset);
|
|
return flipBytes? Character.reverseBytes(value) : value;
|
|
}
|
|
return loadCharUnaligned(offset);
|
|
}
|
|
|
|
private char loadCharUnaligned(long offset) {
|
|
final char value;
|
|
Object b = base;
|
|
if ((offset & 1) == 0) {
|
|
value = PlatformDependent.getChar(b, offset);
|
|
} else {
|
|
value = (char) (PlatformDependent.getByte(b, offset) << 8 |
|
|
PlatformDependent.getByte(b, offset + 1));
|
|
}
|
|
return flipBytes? Character.reverseBytes(value) : value;
|
|
}
|
|
|
|
private short loadShort(long offset) {
|
|
if (ACCESS_UNALIGNED) {
|
|
var value = PlatformDependent.getShort(base, offset);
|
|
return flipBytes? Short.reverseBytes(value) : value;
|
|
}
|
|
return loadShortUnaligned(offset);
|
|
}
|
|
|
|
private short loadShortUnaligned(long offset) {
|
|
final short value;
|
|
Object b = base;
|
|
if ((offset & 1) == 0) {
|
|
value = PlatformDependent.getShort(b, offset);
|
|
} else {
|
|
value = (short) (PlatformDependent.getByte(b, offset) << 8 |
|
|
PlatformDependent.getByte(b, offset + 1));
|
|
}
|
|
return flipBytes? Short.reverseBytes(value) : value;
|
|
}
|
|
|
|
private int loadInt(long offset) {
|
|
if (ACCESS_UNALIGNED) {
|
|
var value = PlatformDependent.getInt(base, offset);
|
|
return flipBytes? Integer.reverseBytes(value) : value;
|
|
}
|
|
return loadIntUnaligned(offset);
|
|
}
|
|
|
|
private int loadIntUnaligned(long offset) {
|
|
final int value;
|
|
Object b = base;
|
|
if ((offset & 3) == 0) {
|
|
value = PlatformDependent.getInt(b, offset);
|
|
} else if ((offset & 1) == 0) {
|
|
value = PlatformDependent.getShort(b, offset) << 16 |
|
|
PlatformDependent.getShort(b, offset + 2);
|
|
} else {
|
|
value = PlatformDependent.getByte(b, offset) << 24 |
|
|
PlatformDependent.getByte(b, offset + 1) << 16 |
|
|
PlatformDependent.getByte(b, offset + 2) << 8 |
|
|
PlatformDependent.getByte(b, offset + 3);
|
|
}
|
|
return flipBytes? Integer.reverseBytes(value) : value;
|
|
}
|
|
|
|
private float loadFloat(long offset) {
|
|
if (ACCESS_UNALIGNED) {
|
|
if (flipBytes) {
|
|
var value = PlatformDependent.getInt(base, offset);
|
|
return Float.intBitsToFloat(Integer.reverseBytes(value));
|
|
}
|
|
return PlatformDependent.getFloat(base, offset);
|
|
}
|
|
return loadFloatUnaligned(offset);
|
|
}
|
|
|
|
private float loadFloatUnaligned(long offset) {
|
|
return Float.intBitsToFloat(loadIntUnaligned(offset));
|
|
}
|
|
|
|
private long loadLong(long offset) {
|
|
if (ACCESS_UNALIGNED) {
|
|
var value = PlatformDependent.getLong(base, offset);
|
|
return flipBytes? Long.reverseBytes(value) : value;
|
|
}
|
|
return loadLongUnaligned(offset);
|
|
}
|
|
|
|
private long loadLongUnaligned(long offset) {
|
|
final long value;
|
|
Object b = base;
|
|
if ((offset & 7) == 0) {
|
|
value = PlatformDependent.getLong(b, offset);
|
|
} else if ((offset & 3) == 0) {
|
|
value = (long) PlatformDependent.getInt(b, offset) << 32 |
|
|
PlatformDependent.getInt(b, offset + 4);
|
|
} else if ((offset & 1) == 0) {
|
|
value = (long) PlatformDependent.getShort(b, offset) << 48 |
|
|
(long) PlatformDependent.getShort(b, offset + 2) << 32 |
|
|
(long) PlatformDependent.getShort(b, offset + 4) << 16 |
|
|
PlatformDependent.getShort(b, offset + 6);
|
|
} else {
|
|
value = (long) PlatformDependent.getByte(b, offset) << 54 |
|
|
(long) PlatformDependent.getByte(b, offset + 1) << 48 |
|
|
(long) PlatformDependent.getByte(b, offset + 2) << 40 |
|
|
(long) PlatformDependent.getByte(b, offset + 3) << 32 |
|
|
(long) PlatformDependent.getByte(b, offset + 4) << 24 |
|
|
(long) PlatformDependent.getByte(b, offset + 5) << 16 |
|
|
(long) PlatformDependent.getByte(b, offset + 6) << 8 |
|
|
PlatformDependent.getByte(b, offset + 7);
|
|
}
|
|
return flipBytes? Long.reverseBytes(value) : value;
|
|
}
|
|
|
|
private double loadDouble(long offset) {
|
|
if (ACCESS_UNALIGNED) {
|
|
if (flipBytes) {
|
|
var value = PlatformDependent.getLong(base, offset);
|
|
return Double.longBitsToDouble(Long.reverseBytes(value));
|
|
}
|
|
return PlatformDependent.getDouble(base, offset);
|
|
}
|
|
return loadDoubleUnaligned(offset);
|
|
}
|
|
|
|
private double loadDoubleUnaligned(long offset) {
|
|
return Double.longBitsToDouble(loadLongUnaligned(offset));
|
|
}
|
|
|
|
private void storeByte(long offset, byte value) {
|
|
PlatformDependent.putByte(base, offset, value);
|
|
}
|
|
|
|
private void storeChar(long offset, char value) {
|
|
if (flipBytes) {
|
|
value = Character.reverseBytes(value);
|
|
}
|
|
if (ACCESS_UNALIGNED) {
|
|
PlatformDependent.putChar(base, offset, value);
|
|
} else {
|
|
storeCharUnaligned(offset, value);
|
|
}
|
|
}
|
|
|
|
private void storeCharUnaligned(long offset, char value) {
|
|
Object b = base;
|
|
if ((offset & 1) == 0) {
|
|
PlatformDependent.putChar(b, offset, value);
|
|
} else {
|
|
PlatformDependent.putByte(b, offset, (byte) (value >> 8));
|
|
PlatformDependent.putByte(b, offset + 1, (byte) value);
|
|
}
|
|
}
|
|
|
|
private void storeShort(long offset, short value) {
|
|
if (flipBytes) {
|
|
value = Short.reverseBytes(value);
|
|
}
|
|
if (ACCESS_UNALIGNED) {
|
|
PlatformDependent.putShort(base, offset, value);
|
|
} else {
|
|
storeShortUnaligned(offset, value);
|
|
}
|
|
}
|
|
|
|
private void storeShortUnaligned(long offset, short value) {
|
|
Object b = base;
|
|
if ((offset & 1) == 0) {
|
|
PlatformDependent.putShort(b, offset, value);
|
|
} else {
|
|
PlatformDependent.putByte(b, offset, (byte) (value >> 8));
|
|
PlatformDependent.putByte(b, offset + 1, (byte) value);
|
|
}
|
|
}
|
|
|
|
private void storeInt(long offset, int value) {
|
|
if (flipBytes) {
|
|
value = Integer.reverseBytes(value);
|
|
}
|
|
if (ACCESS_UNALIGNED) {
|
|
PlatformDependent.putInt(base, offset, value);
|
|
} else {
|
|
storeIntUnaligned(offset, value);
|
|
}
|
|
}
|
|
|
|
private void storeIntUnaligned(long offset, int value) {
|
|
Object b = base;
|
|
if ((offset & 3) == 0) {
|
|
PlatformDependent.putInt(b, offset, value);
|
|
} else if ((offset & 1) == 0) {
|
|
PlatformDependent.putShort(b, offset, (short) (value >> 16));
|
|
PlatformDependent.putShort(b, offset + 2, (short) value);
|
|
} else {
|
|
PlatformDependent.putByte(b, offset, (byte) (value >> 24));
|
|
PlatformDependent.putByte(b, offset + 1, (byte) (value >> 16));
|
|
PlatformDependent.putByte(b, offset + 2, (byte) (value >> 8));
|
|
PlatformDependent.putByte(b, offset + 3, (byte) value);
|
|
}
|
|
}
|
|
|
|
private void storeFloat(long offset, float value) {
|
|
storeInt(offset, Float.floatToRawIntBits(value));
|
|
}
|
|
|
|
private void storeLong(long offset, long value) {
|
|
if (flipBytes) {
|
|
value = Long.reverseBytes(value);
|
|
}
|
|
if (ACCESS_UNALIGNED) {
|
|
PlatformDependent.putLong(base, offset, value);
|
|
} else {
|
|
storeLongUnaligned(offset, value);
|
|
}
|
|
}
|
|
|
|
private void storeLongUnaligned(long offset, long value) {
|
|
Object b = base;
|
|
if ((offset & 7) == 0) {
|
|
PlatformDependent.putLong(b, offset, value);
|
|
} else if ((offset & 3) == 0) {
|
|
PlatformDependent.putInt(b, offset, (int) (value >> 32));
|
|
PlatformDependent.putInt(b, offset + 4, (int) value);
|
|
} else if ((offset & 1) == 0) {
|
|
PlatformDependent.putShort(b, offset, (short) (value >> 48));
|
|
PlatformDependent.putShort(b, offset + 16, (short) (value >> 32));
|
|
PlatformDependent.putShort(b, offset + 32, (short) (value >> 16));
|
|
PlatformDependent.putShort(b, offset + 48, (short) value);
|
|
} else {
|
|
PlatformDependent.putByte(b, offset, (byte) (value >> 56));
|
|
PlatformDependent.putByte(b, offset + 1, (byte) (value >> 48));
|
|
PlatformDependent.putByte(b, offset + 2, (byte) (value >> 40));
|
|
PlatformDependent.putByte(b, offset + 3, (byte) (value >> 32));
|
|
PlatformDependent.putByte(b, offset + 4, (byte) (value >> 24));
|
|
PlatformDependent.putByte(b, offset + 5, (byte) (value >> 16));
|
|
PlatformDependent.putByte(b, offset + 6, (byte) (value >> 8));
|
|
PlatformDependent.putByte(b, offset + 7, (byte) value);
|
|
}
|
|
}
|
|
|
|
private void storeDouble(long offset, double value) {
|
|
storeLong(offset, Double.doubleToRawLongBits(value));
|
|
}
|
|
|
|
Object recover() {
|
|
return memory;
|
|
}
|
|
}
|