2020-10-28 14:38:14 +01:00
|
|
|
/*
|
|
|
|
* Copyright 2020 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.
|
|
|
|
*/
|
2020-11-17 15:38:11 +01:00
|
|
|
package io.netty.buffer.api;
|
2020-10-28 14:38:14 +01:00
|
|
|
|
2020-10-30 14:39:50 +01:00
|
|
|
import java.nio.ByteBuffer;
|
2020-10-28 14:38:14 +01:00
|
|
|
import java.nio.ByteOrder;
|
|
|
|
import java.util.Arrays;
|
2020-12-03 17:48:28 +01:00
|
|
|
import java.util.Objects;
|
2020-10-28 14:38:14 +01:00
|
|
|
|
|
|
|
final class CompositeBuf extends RcSupport<Buf, CompositeBuf> implements Buf {
|
|
|
|
/**
|
|
|
|
* The max array size is JVM implementation dependant, but most seem to settle on {@code Integer.MAX_VALUE - 8}.
|
|
|
|
* We set the max composite buffer capacity to the same, since it would otherwise be impossible to create a
|
|
|
|
* non-composite copy of the buffer.
|
|
|
|
*/
|
|
|
|
private static final int MAX_CAPACITY = Integer.MAX_VALUE - 8;
|
2020-12-08 19:25:53 +01:00
|
|
|
private static final Drop<CompositeBuf> COMPOSITE_DROP = buf -> {
|
|
|
|
for (Buf b : buf.bufs) {
|
|
|
|
b.close();
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
2020-12-14 14:07:11 +01:00
|
|
|
buf.makeInaccessible();
|
2020-10-28 14:38:14 +01:00
|
|
|
};
|
|
|
|
|
2020-11-16 18:00:32 +01:00
|
|
|
private final Allocator allocator;
|
2020-10-28 14:38:14 +01:00
|
|
|
private final TornBufAccessors tornBufAccessors;
|
|
|
|
private final boolean isSendable;
|
2020-11-16 18:00:32 +01:00
|
|
|
private Buf[] bufs;
|
|
|
|
private int[] offsets; // The offset, for the composite buffer, where each constituent buffer starts.
|
|
|
|
private int capacity;
|
2020-10-28 14:38:14 +01:00
|
|
|
private int roff;
|
|
|
|
private int woff;
|
|
|
|
private int subOffset; // The next offset *within* a consituent buffer to read from or write to.
|
2020-12-08 19:25:53 +01:00
|
|
|
private ByteOrder order;
|
2020-12-14 14:07:11 +01:00
|
|
|
private boolean closed;
|
2021-01-05 16:53:21 +01:00
|
|
|
private boolean readOnly;
|
2020-10-28 14:38:14 +01:00
|
|
|
|
2020-11-16 18:00:32 +01:00
|
|
|
CompositeBuf(Allocator allocator, Buf[] bufs) {
|
2021-01-15 15:54:03 +01:00
|
|
|
this(allocator, true, filterExternalBufs(bufs), COMPOSITE_DROP);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Buf[] filterExternalBufs(Buf[] bufs) {
|
|
|
|
// Allocating a new array unconditionally also prevents external modification of the array.
|
|
|
|
return Arrays.stream(bufs).filter(b -> b.capacity() > 0).toArray(Buf[]::new);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
2020-11-16 18:00:32 +01:00
|
|
|
private CompositeBuf(Allocator allocator, boolean isSendable, Buf[] bufs, Drop<CompositeBuf> drop) {
|
2020-10-30 14:21:20 +01:00
|
|
|
super(drop);
|
2020-11-16 18:00:32 +01:00
|
|
|
this.allocator = allocator;
|
2020-10-28 14:38:14 +01:00
|
|
|
this.isSendable = isSendable;
|
|
|
|
for (Buf buf : bufs) {
|
|
|
|
buf.acquire();
|
|
|
|
}
|
|
|
|
if (bufs.length > 0) {
|
|
|
|
ByteOrder targetOrder = bufs[0].order();
|
|
|
|
for (Buf buf : bufs) {
|
|
|
|
if (buf.order() != targetOrder) {
|
|
|
|
throw new IllegalArgumentException("Constituent buffers have inconsistent byte order.");
|
|
|
|
}
|
|
|
|
}
|
2020-12-08 19:25:53 +01:00
|
|
|
order = bufs[0].order();
|
2021-01-05 16:53:21 +01:00
|
|
|
|
|
|
|
boolean targetReadOnly = bufs[0].readOnly();
|
|
|
|
for (Buf buf : bufs) {
|
|
|
|
if (buf.readOnly() != targetReadOnly) {
|
|
|
|
throw new IllegalArgumentException("Constituent buffers have inconsistent read-only state.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
readOnly = targetReadOnly;
|
2020-12-08 19:25:53 +01:00
|
|
|
} else {
|
|
|
|
order = ByteOrder.nativeOrder();
|
2020-12-03 17:48:28 +01:00
|
|
|
}
|
|
|
|
this.bufs = bufs;
|
|
|
|
computeBufferOffsets();
|
|
|
|
tornBufAccessors = new TornBufAccessors(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void computeBufferOffsets() {
|
|
|
|
if (bufs.length > 0) {
|
|
|
|
int woff = 0;
|
|
|
|
int roff = 0;
|
2020-10-28 14:38:14 +01:00
|
|
|
boolean woffMidpoint = false;
|
|
|
|
for (Buf buf : bufs) {
|
|
|
|
if (buf.writableBytes() == 0) {
|
|
|
|
woff += buf.capacity();
|
|
|
|
} else if (!woffMidpoint) {
|
2020-11-11 23:00:08 +01:00
|
|
|
woff += buf.writerOffset();
|
2020-10-28 14:38:14 +01:00
|
|
|
woffMidpoint = true;
|
2020-11-11 23:00:08 +01:00
|
|
|
} else if (buf.writerOffset() != 0) {
|
2020-10-28 14:38:14 +01:00
|
|
|
throw new IllegalArgumentException(
|
2020-12-03 17:48:28 +01:00
|
|
|
"The given buffers cannot be composed because they leave an unwritten gap: " +
|
2020-10-28 14:38:14 +01:00
|
|
|
Arrays.toString(bufs) + '.');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
boolean roffMidpoint = false;
|
|
|
|
for (Buf buf : bufs) {
|
|
|
|
if (buf.readableBytes() == 0 && buf.writableBytes() == 0) {
|
|
|
|
roff += buf.capacity();
|
|
|
|
} else if (!roffMidpoint) {
|
2020-11-11 23:00:08 +01:00
|
|
|
roff += buf.readerOffset();
|
2020-10-28 14:38:14 +01:00
|
|
|
roffMidpoint = true;
|
2020-11-11 23:00:08 +01:00
|
|
|
} else if (buf.readerOffset() != 0) {
|
2020-10-28 14:38:14 +01:00
|
|
|
throw new IllegalArgumentException(
|
2020-12-03 17:48:28 +01:00
|
|
|
"The given buffers cannot be composed because they leave an unread gap: " +
|
2020-10-28 14:38:14 +01:00
|
|
|
Arrays.toString(bufs) + '.');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert roff <= woff:
|
|
|
|
"The given buffers place the read offset ahead of the write offset: " + Arrays.toString(bufs) + '.';
|
2020-12-03 17:48:28 +01:00
|
|
|
// Commit computed offsets.
|
|
|
|
this.woff = woff;
|
|
|
|
this.roff = roff;
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
2020-11-16 18:00:32 +01:00
|
|
|
|
2020-10-28 14:38:14 +01:00
|
|
|
offsets = new int[bufs.length];
|
|
|
|
long cap = 0;
|
|
|
|
for (int i = 0; i < bufs.length; i++) {
|
|
|
|
offsets[i] = (int) cap;
|
|
|
|
cap += bufs[i].capacity();
|
|
|
|
}
|
|
|
|
if (cap > MAX_CAPACITY) {
|
|
|
|
throw new IllegalArgumentException(
|
|
|
|
"Combined size of the constituent buffers is too big. " +
|
|
|
|
"The maximum buffer capacity is " + MAX_CAPACITY + " (Interger.MAX_VALUE - 8), " +
|
|
|
|
"but the sum of the constituent buffer capacities was " + cap + '.');
|
|
|
|
}
|
|
|
|
capacity = (int) cap;
|
|
|
|
}
|
|
|
|
|
2020-11-20 12:44:09 +01:00
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return "Buf[roff:" + roff + ", woff:" + woff + ", cap:" + capacity + ']';
|
|
|
|
}
|
|
|
|
|
2020-10-28 14:38:14 +01:00
|
|
|
@Override
|
|
|
|
public Buf order(ByteOrder order) {
|
2020-12-10 12:51:18 +01:00
|
|
|
if (this.order != order) {
|
|
|
|
this.order = order;
|
|
|
|
for (Buf buf : bufs) {
|
|
|
|
buf.order(order);
|
|
|
|
}
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ByteOrder order() {
|
2020-12-08 19:25:53 +01:00
|
|
|
return order;
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int capacity() {
|
|
|
|
return capacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public int readerOffset() {
|
2020-10-28 14:38:14 +01:00
|
|
|
return roff;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf readerOffset(int index) {
|
2020-10-28 14:38:14 +01:00
|
|
|
prepRead(index, 0);
|
|
|
|
int indexLeft = index;
|
|
|
|
for (Buf buf : bufs) {
|
2020-11-11 23:00:08 +01:00
|
|
|
buf.readerOffset(Math.min(indexLeft, buf.capacity()));
|
2020-10-28 14:38:14 +01:00
|
|
|
indexLeft = Math.max(0, indexLeft - buf.capacity());
|
|
|
|
}
|
|
|
|
roff = index;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public int writerOffset() {
|
2020-10-28 14:38:14 +01:00
|
|
|
return woff;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf writerOffset(int index) {
|
2020-10-28 14:38:14 +01:00
|
|
|
checkWriteBounds(index, 0);
|
|
|
|
int indexLeft = index;
|
|
|
|
for (Buf buf : bufs) {
|
2020-11-11 23:00:08 +01:00
|
|
|
buf.writerOffset(Math.min(indexLeft, buf.capacity()));
|
2020-10-28 14:38:14 +01:00
|
|
|
indexLeft = Math.max(0, indexLeft - buf.capacity());
|
|
|
|
}
|
|
|
|
woff = index;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf fill(byte value) {
|
|
|
|
for (Buf buf : bufs) {
|
|
|
|
buf.fill(value);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2021-01-11 16:10:00 +01:00
|
|
|
public long nativeAddress() {
|
2020-10-28 14:38:14 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-01-05 16:53:21 +01:00
|
|
|
@Override
|
|
|
|
public Buf readOnly(boolean readOnly) {
|
|
|
|
for (Buf buf : bufs) {
|
|
|
|
buf.readOnly(readOnly);
|
|
|
|
}
|
|
|
|
this.readOnly = readOnly;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean readOnly() {
|
|
|
|
return readOnly;
|
|
|
|
}
|
|
|
|
|
2020-10-28 14:38:14 +01:00
|
|
|
@Override
|
|
|
|
public Buf slice(int offset, int length) {
|
|
|
|
checkWriteBounds(offset, length);
|
|
|
|
if (offset < 0 || length < 0) {
|
2020-11-12 14:55:42 +01:00
|
|
|
throw new IllegalArgumentException(
|
2020-10-28 14:38:14 +01:00
|
|
|
"Offset and length cannot be negative, but offset was " +
|
|
|
|
offset + ", and length was " + length + '.');
|
|
|
|
}
|
|
|
|
Buf choice = (Buf) chooseBuffer(offset, 0);
|
|
|
|
Buf[] slices = null;
|
2020-10-30 14:21:20 +01:00
|
|
|
acquire(); // Increase reference count of the original composite buffer.
|
|
|
|
Drop<CompositeBuf> drop = obj -> {
|
|
|
|
close(); // Decrement the reference count of the original composite buffer.
|
|
|
|
COMPOSITE_DROP.drop(obj);
|
|
|
|
};
|
2020-10-28 14:38:14 +01:00
|
|
|
|
|
|
|
try {
|
|
|
|
if (length > 0) {
|
|
|
|
slices = new Buf[bufs.length];
|
|
|
|
int off = subOffset;
|
|
|
|
int cap = length;
|
|
|
|
int i;
|
|
|
|
for (i = searchOffsets(offset); cap > 0; i++) {
|
|
|
|
var buf = bufs[i];
|
|
|
|
int avail = buf.capacity() - off;
|
|
|
|
slices[i] = buf.slice(off, Math.min(cap, avail));
|
|
|
|
cap -= avail;
|
|
|
|
off = 0;
|
|
|
|
}
|
|
|
|
slices = Arrays.copyOf(slices, i);
|
|
|
|
} else {
|
|
|
|
// Specialize for length == 0, since we must slice from at least one constituent buffer.
|
|
|
|
slices = new Buf[] { choice.slice(subOffset, 0) };
|
|
|
|
}
|
|
|
|
|
2021-01-05 16:53:21 +01:00
|
|
|
return new CompositeBuf(allocator, false, slices, drop);
|
2020-10-30 14:21:20 +01:00
|
|
|
} catch (Throwable throwable) {
|
|
|
|
// We called acquire prior to the try-clause. We need to undo that if we're not creating a composite buffer:
|
|
|
|
close();
|
|
|
|
throw throwable;
|
2020-10-28 14:38:14 +01:00
|
|
|
} finally {
|
|
|
|
if (slices != null) {
|
|
|
|
for (Buf slice : slices) {
|
|
|
|
if (slice != null) {
|
|
|
|
slice.close(); // Ownership now transfers to the composite buffer.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-30 14:39:50 +01:00
|
|
|
@Override
|
|
|
|
public void copyInto(int srcPos, byte[] dest, int destPos, int length) {
|
|
|
|
copyInto(srcPos, (b, s, d, l) -> b.copyInto(s, dest, d, l), destPos, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void copyInto(int srcPos, ByteBuffer dest, int destPos, int length) {
|
|
|
|
copyInto(srcPos, (b, s, d, l) -> b.copyInto(s, dest, d, l), destPos, length);
|
|
|
|
}
|
|
|
|
|
2020-11-06 13:39:21 +01:00
|
|
|
private void copyInto(int srcPos, CopyInto dest, int destPos, int length) {
|
|
|
|
if (length < 0) {
|
|
|
|
throw new IndexOutOfBoundsException("Length cannot be negative: " + length + '.');
|
|
|
|
}
|
|
|
|
if (srcPos < 0) {
|
2021-01-05 16:53:21 +01:00
|
|
|
throw indexOutOfBounds(srcPos, false);
|
2020-11-06 13:39:21 +01:00
|
|
|
}
|
|
|
|
if (srcPos + length > capacity) {
|
2021-01-05 16:53:21 +01:00
|
|
|
throw indexOutOfBounds(srcPos + length, false);
|
2020-11-06 13:39:21 +01:00
|
|
|
}
|
|
|
|
while (length > 0) {
|
|
|
|
var buf = (Buf) chooseBuffer(srcPos, 0);
|
2020-11-12 14:55:42 +01:00
|
|
|
int toCopy = Math.min(buf.capacity() - subOffset, length);
|
2020-11-06 13:39:21 +01:00
|
|
|
dest.copyInto(buf, subOffset, destPos, toCopy);
|
|
|
|
srcPos += toCopy;
|
|
|
|
destPos += toCopy;
|
|
|
|
length -= toCopy;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@FunctionalInterface
|
|
|
|
private interface CopyInto {
|
|
|
|
void copyInto(Buf src, int srcPos, int destPos, int length);
|
|
|
|
}
|
|
|
|
|
2020-10-30 14:39:50 +01:00
|
|
|
@Override
|
|
|
|
public void copyInto(int srcPos, Buf dest, int destPos, int length) {
|
|
|
|
if (length < 0) {
|
|
|
|
throw new IndexOutOfBoundsException("Length cannot be negative: " + length + '.');
|
|
|
|
}
|
|
|
|
if (srcPos < 0) {
|
2021-01-05 16:53:21 +01:00
|
|
|
throw indexOutOfBounds(srcPos, false);
|
2020-10-30 14:39:50 +01:00
|
|
|
}
|
|
|
|
if (srcPos + length > capacity) {
|
2021-01-05 16:53:21 +01:00
|
|
|
throw indexOutOfBounds(srcPos + length, false);
|
2020-10-30 14:39:50 +01:00
|
|
|
}
|
2020-11-06 13:39:21 +01:00
|
|
|
|
|
|
|
// Iterate in reverse to account for src and dest buffer overlap.
|
|
|
|
// todo optimise by delegating to constituent buffers.
|
2020-12-07 14:56:03 +01:00
|
|
|
var cursor = openReverseCursor(srcPos + length - 1, length);
|
2020-11-06 13:39:21 +01:00
|
|
|
ByteOrder prevOrder = dest.order();
|
|
|
|
// We read longs in BE, in reverse, so they need to be flipped for writing.
|
|
|
|
dest.order(ByteOrder.LITTLE_ENDIAN);
|
|
|
|
try {
|
2020-12-07 14:56:03 +01:00
|
|
|
while (cursor.readLong()) {
|
2020-11-06 13:39:21 +01:00
|
|
|
length -= Long.BYTES;
|
2020-12-02 14:29:40 +01:00
|
|
|
dest.setLong(destPos + length, cursor.getLong());
|
2020-11-06 13:39:21 +01:00
|
|
|
}
|
2020-12-07 14:56:03 +01:00
|
|
|
while (cursor.readByte()) {
|
2020-12-02 14:29:40 +01:00
|
|
|
dest.setByte(destPos + --length, cursor.getByte());
|
2020-11-06 13:39:21 +01:00
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
dest.order(prevOrder);
|
2020-10-30 14:39:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-05 15:15:34 +01:00
|
|
|
@Override
|
2020-12-07 14:56:03 +01:00
|
|
|
public ByteCursor openCursor(int fromOffset, int length) {
|
2020-11-06 13:39:21 +01:00
|
|
|
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 + '.');
|
|
|
|
}
|
2020-11-05 15:15:34 +01:00
|
|
|
int startBufferIndex = searchOffsets(fromOffset);
|
|
|
|
int off = fromOffset - offsets[startBufferIndex];
|
|
|
|
Buf startBuf = bufs[startBufferIndex];
|
2020-12-07 14:56:03 +01:00
|
|
|
ByteCursor startCursor = startBuf.openCursor(off, Math.min(startBuf.capacity() - off, length));
|
2020-12-02 14:29:40 +01:00
|
|
|
return new ByteCursor() {
|
2020-11-05 15:15:34 +01:00
|
|
|
int index = fromOffset;
|
|
|
|
final int end = fromOffset + length;
|
|
|
|
int bufferIndex = startBufferIndex;
|
2020-12-07 16:00:45 +01:00
|
|
|
int initOffset = startCursor.currentOffset();
|
2020-12-02 14:29:40 +01:00
|
|
|
ByteCursor cursor = startCursor;
|
|
|
|
long longValue = -1;
|
|
|
|
byte byteValue = -1;
|
2020-11-05 15:15:34 +01:00
|
|
|
|
|
|
|
@Override
|
2020-12-07 14:56:03 +01:00
|
|
|
public boolean readLong() {
|
2020-12-07 16:00:45 +01:00
|
|
|
if (cursor.readLong()) {
|
|
|
|
longValue = cursor.getLong();
|
|
|
|
return true;
|
|
|
|
}
|
2020-12-02 14:29:40 +01:00
|
|
|
if (bytesLeft() >= Long.BYTES) {
|
2020-12-07 16:00:45 +01:00
|
|
|
longValue = nextLongFromBytes();
|
2020-12-02 14:29:40 +01:00
|
|
|
return true;
|
2020-11-05 15:15:34 +01:00
|
|
|
}
|
2020-12-02 14:29:40 +01:00
|
|
|
return false;
|
2020-11-05 15:15:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private long nextLongFromBytes() {
|
2020-12-07 16:00:45 +01:00
|
|
|
if (cursor.bytesLeft() == 0) {
|
|
|
|
nextCursor();
|
|
|
|
if (cursor.readLong()) {
|
|
|
|
return cursor.getLong();
|
|
|
|
}
|
|
|
|
}
|
2020-11-05 15:15:34 +01:00
|
|
|
long val = 0;
|
|
|
|
for (int i = 0; i < 8; i++) {
|
2020-12-07 14:56:03 +01:00
|
|
|
readByte();
|
2020-11-05 15:15:34 +01:00
|
|
|
val <<= 8;
|
2020-12-02 14:29:40 +01:00
|
|
|
val |= getByte();
|
2020-11-05 15:15:34 +01:00
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-12-02 14:29:40 +01:00
|
|
|
public long getLong() {
|
|
|
|
return longValue;
|
2020-11-05 15:15:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-12-07 14:56:03 +01:00
|
|
|
public boolean readByte() {
|
2020-12-07 16:00:45 +01:00
|
|
|
if (cursor.readByte()) {
|
|
|
|
byteValue = cursor.getByte();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (bytesLeft() > 0) {
|
|
|
|
nextCursor();
|
|
|
|
cursor.readByte();
|
2020-12-02 14:29:40 +01:00
|
|
|
byteValue = cursor.getByte();
|
|
|
|
return true;
|
2020-11-05 15:15:34 +01:00
|
|
|
}
|
2020-12-02 14:29:40 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-07 16:00:45 +01:00
|
|
|
private void nextCursor() {
|
|
|
|
bufferIndex++;
|
|
|
|
Buf nextBuf = bufs[bufferIndex];
|
|
|
|
cursor = nextBuf.openCursor(0, Math.min(nextBuf.capacity(), bytesLeft()));
|
|
|
|
initOffset = 0;
|
|
|
|
}
|
|
|
|
|
2020-12-02 14:29:40 +01:00
|
|
|
@Override
|
|
|
|
public byte getByte() {
|
|
|
|
return byteValue;
|
2020-11-05 15:15:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int currentOffset() {
|
2020-12-07 16:00:45 +01:00
|
|
|
int currOff = cursor.currentOffset();
|
|
|
|
index += currOff - initOffset;
|
|
|
|
initOffset = currOff;
|
2020-11-05 15:15:34 +01:00
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int bytesLeft() {
|
2020-12-07 16:00:45 +01:00
|
|
|
return end - currentOffset();
|
2020-11-05 15:15:34 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-11-06 13:39:21 +01:00
|
|
|
@Override
|
2020-12-07 14:56:03 +01:00
|
|
|
public ByteCursor openReverseCursor(int fromOffset, int length) {
|
2020-11-06 13:39:21 +01:00
|
|
|
if (fromOffset < 0) {
|
|
|
|
throw new IllegalArgumentException("The fromOffset cannot be negative: " + fromOffset + '.');
|
2020-10-30 14:39:50 +01:00
|
|
|
}
|
2020-11-06 13:39:21 +01:00
|
|
|
if (length < 0) {
|
|
|
|
throw new IllegalArgumentException("The length cannot be negative: " + length + '.');
|
2020-10-30 14:39:50 +01:00
|
|
|
}
|
2020-11-06 13:39:21 +01:00
|
|
|
if (fromOffset - length < -1) {
|
|
|
|
throw new IllegalArgumentException("The fromOffset-length would underflow the buffer: " +
|
|
|
|
"fromOffset=" + fromOffset + ", length=" + length + '.');
|
2020-10-30 14:39:50 +01:00
|
|
|
}
|
2020-11-06 13:39:21 +01:00
|
|
|
int startBufferIndex = searchOffsets(fromOffset);
|
|
|
|
int off = fromOffset - offsets[startBufferIndex];
|
|
|
|
Buf startBuf = bufs[startBufferIndex];
|
2020-12-07 14:56:03 +01:00
|
|
|
ByteCursor startCursor = startBuf.openReverseCursor(off, Math.min(off + 1, length));
|
2020-12-02 14:29:40 +01:00
|
|
|
return new ByteCursor() {
|
2020-11-06 13:39:21 +01:00
|
|
|
int index = fromOffset;
|
|
|
|
final int end = fromOffset - length;
|
|
|
|
int bufferIndex = startBufferIndex;
|
2020-12-07 16:00:45 +01:00
|
|
|
int initOffset = startCursor.currentOffset();
|
2020-12-02 14:29:40 +01:00
|
|
|
ByteCursor cursor = startCursor;
|
|
|
|
long longValue = -1;
|
|
|
|
byte byteValue = -1;
|
2020-10-30 14:39:50 +01:00
|
|
|
|
2020-11-06 13:39:21 +01:00
|
|
|
@Override
|
2020-12-07 14:56:03 +01:00
|
|
|
public boolean readLong() {
|
2020-12-07 16:00:45 +01:00
|
|
|
if (cursor.readLong()) {
|
|
|
|
longValue = cursor.getLong();
|
|
|
|
return true;
|
|
|
|
}
|
2020-12-02 14:29:40 +01:00
|
|
|
if (bytesLeft() >= Long.BYTES) {
|
2020-12-07 16:00:45 +01:00
|
|
|
longValue = nextLongFromBytes();
|
2020-12-02 14:29:40 +01:00
|
|
|
return true;
|
2020-11-06 13:39:21 +01:00
|
|
|
}
|
2020-12-02 14:29:40 +01:00
|
|
|
return false;
|
2020-11-06 13:39:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private long nextLongFromBytes() {
|
2020-12-07 16:00:45 +01:00
|
|
|
if (cursor.bytesLeft() == 0) {
|
|
|
|
nextCursor();
|
|
|
|
if (cursor.readLong()) {
|
|
|
|
return cursor.getLong();
|
|
|
|
}
|
|
|
|
}
|
2020-11-06 13:39:21 +01:00
|
|
|
long val = 0;
|
|
|
|
for (int i = 0; i < 8; i++) {
|
2020-12-07 14:56:03 +01:00
|
|
|
readByte();
|
2020-11-06 13:39:21 +01:00
|
|
|
val <<= 8;
|
2020-12-02 14:29:40 +01:00
|
|
|
val |= getByte();
|
2020-11-06 13:39:21 +01:00
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-12-02 14:29:40 +01:00
|
|
|
public long getLong() {
|
|
|
|
return longValue;
|
2020-11-06 13:39:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-12-07 14:56:03 +01:00
|
|
|
public boolean readByte() {
|
2020-12-07 16:00:45 +01:00
|
|
|
if (cursor.readByte()) {
|
|
|
|
byteValue = cursor.getByte();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (bytesLeft() > 0) {
|
|
|
|
nextCursor();
|
|
|
|
cursor.readByte();
|
2020-12-02 14:29:40 +01:00
|
|
|
byteValue = cursor.getByte();
|
|
|
|
return true;
|
2020-11-06 13:39:21 +01:00
|
|
|
}
|
2020-12-02 14:29:40 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-07 16:00:45 +01:00
|
|
|
private void nextCursor() {
|
|
|
|
bufferIndex--;
|
|
|
|
Buf nextBuf = bufs[bufferIndex];
|
|
|
|
int length = Math.min(nextBuf.capacity(), bytesLeft());
|
|
|
|
int offset = nextBuf.capacity() - 1;
|
|
|
|
cursor = nextBuf.openReverseCursor(offset, length);
|
|
|
|
initOffset = offset;
|
|
|
|
}
|
|
|
|
|
2020-12-02 14:29:40 +01:00
|
|
|
@Override
|
|
|
|
public byte getByte() {
|
|
|
|
return byteValue;
|
2020-11-06 13:39:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int currentOffset() {
|
2020-12-07 16:00:45 +01:00
|
|
|
int currOff = cursor.currentOffset();
|
|
|
|
index -= initOffset - currOff;
|
|
|
|
initOffset = currOff;
|
2020-11-06 13:39:21 +01:00
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int bytesLeft() {
|
2020-12-07 16:00:45 +01:00
|
|
|
return currentOffset() - end;
|
2020-11-06 13:39:21 +01:00
|
|
|
}
|
|
|
|
};
|
2020-10-30 14:39:50 +01:00
|
|
|
}
|
|
|
|
|
2020-11-16 18:00:32 +01:00
|
|
|
@Override
|
2020-12-16 16:44:20 +01:00
|
|
|
public void ensureWritable(int size, boolean allowCompaction) {
|
2020-11-16 18:00:32 +01:00
|
|
|
if (!isOwned()) {
|
|
|
|
throw 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 + '.');
|
|
|
|
}
|
2021-01-05 16:53:21 +01:00
|
|
|
if (readOnly) {
|
|
|
|
throw bufferIsReadOnly();
|
|
|
|
}
|
2020-12-16 16:44:20 +01:00
|
|
|
if (writableBytes() >= size) {
|
|
|
|
// We already have enough space.
|
|
|
|
return;
|
2020-11-16 18:00:32 +01:00
|
|
|
}
|
2020-12-16 16:44:20 +01:00
|
|
|
|
|
|
|
if (allowCompaction && size <= roff) {
|
|
|
|
// Let's see if we can solve some or all of the requested size with compaction.
|
|
|
|
// We always compact as much as is possible, regardless of size. This amortizes our work.
|
|
|
|
int compactableBuffers = 0;
|
|
|
|
for (Buf buf : bufs) {
|
|
|
|
if (buf.capacity() != buf.readerOffset()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
compactableBuffers++;
|
|
|
|
}
|
|
|
|
if (compactableBuffers > 0) {
|
|
|
|
Buf[] compactable;
|
|
|
|
if (compactableBuffers < bufs.length) {
|
|
|
|
compactable = new Buf[compactableBuffers];
|
|
|
|
System.arraycopy(bufs, 0, compactable, 0, compactable.length);
|
|
|
|
System.arraycopy(bufs, compactable.length, bufs, 0, bufs.length - compactable.length);
|
|
|
|
System.arraycopy(compactable, 0, bufs, bufs.length - compactable.length, compactable.length);
|
|
|
|
} else {
|
|
|
|
compactable = bufs;
|
|
|
|
}
|
|
|
|
for (Buf buf : compactable) {
|
|
|
|
buf.reset();
|
|
|
|
}
|
|
|
|
computeBufferOffsets();
|
|
|
|
if (writableBytes() >= size) {
|
|
|
|
// Now we have enough space.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
long newSize = capacity() + (long) size;
|
|
|
|
Allocator.checkSize(newSize);
|
|
|
|
int growth = size - writableBytes();
|
|
|
|
Buf extension = bufs.length == 0? allocator.allocate(growth) : allocator.allocate(growth, order());
|
|
|
|
unsafeExtendWith(extension);
|
2020-11-16 18:00:32 +01:00
|
|
|
}
|
|
|
|
|
2020-12-03 17:48:28 +01:00
|
|
|
void extendWith(Buf extension) {
|
|
|
|
Objects.requireNonNull(extension, "Extension buffer cannot be null.");
|
|
|
|
if (!isOwned()) {
|
|
|
|
throw new IllegalStateException("This buffer cannot be extended because it is not in an owned state.");
|
|
|
|
}
|
|
|
|
if (extension == this) {
|
|
|
|
throw new IllegalArgumentException("This buffer cannot be extended with itself.");
|
|
|
|
}
|
|
|
|
if (bufs.length > 0 && extension.order() != order()) {
|
|
|
|
throw new IllegalArgumentException(
|
|
|
|
"This buffer uses " + order() + " byte order, and cannot be extended with " +
|
|
|
|
"a buffer that uses " + extension.order() + " byte order.");
|
|
|
|
}
|
2021-01-05 16:53:21 +01:00
|
|
|
if (bufs.length > 0 && extension.readOnly() != readOnly()) {
|
|
|
|
throw new IllegalArgumentException(
|
|
|
|
"This buffer is " + (readOnly? "read-only" : "writable") + ", " +
|
|
|
|
"and cannot be extended with a buffer that is " +
|
|
|
|
(extension.readOnly()? "read-only." : "writable."));
|
2020-12-08 19:25:53 +01:00
|
|
|
}
|
2021-01-05 16:53:21 +01:00
|
|
|
|
2021-01-15 15:54:03 +01:00
|
|
|
long extensionCapacity = extension.capacity();
|
|
|
|
if (extensionCapacity == 0) {
|
|
|
|
// Extending by a zero-sized buffer makes no difference. Especially since it's not allowed to change the
|
|
|
|
// capacity of buffers that are constiuents of composite buffers.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
long newSize = capacity() + extensionCapacity;
|
2020-12-03 17:48:28 +01:00
|
|
|
Allocator.checkSize(newSize);
|
|
|
|
|
|
|
|
Buf[] restoreTemp = bufs; // We need this to restore our buffer array, in case offset computations fail.
|
|
|
|
try {
|
|
|
|
unsafeExtendWith(extension.acquire());
|
2021-01-05 16:53:21 +01:00
|
|
|
if (restoreTemp.length == 0) {
|
|
|
|
order = extension.order();
|
|
|
|
readOnly = extension.readOnly();
|
|
|
|
}
|
2020-12-03 17:48:28 +01:00
|
|
|
} catch (Exception e) {
|
|
|
|
bufs = restoreTemp;
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void unsafeExtendWith(Buf extension) {
|
|
|
|
bufs = Arrays.copyOf(bufs, bufs.length + 1);
|
|
|
|
bufs[bufs.length - 1] = extension;
|
|
|
|
computeBufferOffsets();
|
|
|
|
}
|
|
|
|
|
2020-12-08 19:25:53 +01:00
|
|
|
@Override
|
|
|
|
public Buf bifurcate() {
|
|
|
|
if (!isOwned()) {
|
|
|
|
throw new IllegalStateException("Cannot bifurcate a buffer that is not owned.");
|
|
|
|
}
|
|
|
|
if (bufs.length == 0) {
|
|
|
|
// Bifurcating a zero-length buffer is trivial.
|
|
|
|
return new CompositeBuf(allocator, true, bufs, unsafeGetDrop()).order(order);
|
|
|
|
}
|
|
|
|
|
|
|
|
int i = searchOffsets(woff);
|
|
|
|
int off = woff - offsets[i];
|
|
|
|
Buf[] bifs = Arrays.copyOf(bufs, off == 0? i : 1 + i);
|
|
|
|
bufs = Arrays.copyOfRange(bufs, off == bufs[i].capacity()? 1 + i : i, bufs.length);
|
|
|
|
if (off > 0 && bifs.length > 0 && off < bifs[bifs.length - 1].capacity()) {
|
|
|
|
bifs[bifs.length - 1] = bufs[0].bifurcate();
|
|
|
|
}
|
|
|
|
computeBufferOffsets();
|
|
|
|
try {
|
|
|
|
var compositeBuf = new CompositeBuf(allocator, true, bifs, unsafeGetDrop());
|
|
|
|
compositeBuf.order = order; // Preserve byte order even if bifs array is empty.
|
|
|
|
return compositeBuf;
|
|
|
|
} finally {
|
|
|
|
// Drop our references to the buffers in the bifs array. They belong to the new composite buffer now.
|
|
|
|
for (Buf bif : bifs) {
|
|
|
|
bif.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-14 17:01:39 +01:00
|
|
|
@Override
|
|
|
|
public void compact() {
|
|
|
|
if (!isOwned()) {
|
|
|
|
throw new IllegalStateException("Buffer must be owned in order to compact.");
|
|
|
|
}
|
2021-01-05 16:53:21 +01:00
|
|
|
if (readOnly()) {
|
|
|
|
throw new IllegalStateException("Buffer must be writable in order to compact, but was read-only.");
|
|
|
|
}
|
2020-12-14 17:01:39 +01:00
|
|
|
int distance = roff;
|
|
|
|
if (distance == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int pos = 0;
|
|
|
|
var oldOrder = order;
|
|
|
|
order = ByteOrder.BIG_ENDIAN;
|
|
|
|
try {
|
|
|
|
var cursor = openCursor();
|
|
|
|
while (cursor.readLong()) {
|
|
|
|
setLong(pos, cursor.getLong());
|
|
|
|
pos += Long.BYTES;
|
|
|
|
}
|
|
|
|
while (cursor.readByte()) {
|
|
|
|
setByte(pos, cursor.getByte());
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
order = oldOrder;
|
|
|
|
}
|
|
|
|
readerOffset(0);
|
|
|
|
writerOffset(woff - distance);
|
|
|
|
}
|
|
|
|
|
2021-01-11 16:10:00 +01:00
|
|
|
@Override
|
2021-01-15 15:54:03 +01:00
|
|
|
public int countComponents() {
|
2021-01-11 16:10:00 +01:00
|
|
|
int sum = 0;
|
|
|
|
for (Buf buf : bufs) {
|
2021-01-15 15:54:03 +01:00
|
|
|
sum += buf.countComponents();
|
2021-01-11 16:10:00 +01:00
|
|
|
}
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2021-01-15 15:54:03 +01:00
|
|
|
public int countReadableComponents() {
|
|
|
|
int sum = 0;
|
|
|
|
for (Buf buf : bufs) {
|
|
|
|
sum += buf.countReadableComponents();
|
|
|
|
}
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int countWritableComponents() {
|
|
|
|
int sum = 0;
|
|
|
|
for (Buf buf : bufs) {
|
|
|
|
sum += buf.countWritableComponents();
|
|
|
|
}
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2021-01-15 21:32:24 +01:00
|
|
|
public int forEachReadable(int initialIndex, ComponentProcessor.OfReadable processor) {
|
2021-01-11 16:10:00 +01:00
|
|
|
checkReadBounds(readerOffset(), Math.max(1, readableBytes()));
|
|
|
|
int visited = 0;
|
|
|
|
for (Buf buf : bufs) {
|
|
|
|
if (buf.readableBytes() > 0) {
|
2021-01-15 15:54:03 +01:00
|
|
|
int count = buf.forEachReadable(visited + initialIndex, processor);
|
|
|
|
if (count > 0) {
|
|
|
|
visited += count;
|
|
|
|
} else {
|
|
|
|
visited = -visited + count;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return visited;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2021-01-15 21:32:24 +01:00
|
|
|
public int forEachWritable(int initialIndex, ComponentProcessor.OfWritable processor) {
|
2021-01-15 15:54:03 +01:00
|
|
|
checkWriteBounds(writerOffset(), Math.max(1, writableBytes()));
|
|
|
|
int visited = 0;
|
|
|
|
for (Buf buf : bufs) {
|
|
|
|
if (buf.writableBytes() > 0) {
|
|
|
|
int count = buf.forEachWritable(visited + initialIndex, processor);
|
|
|
|
if (count > 0) {
|
|
|
|
visited += count;
|
|
|
|
} else {
|
|
|
|
visited = -visited + count;
|
|
|
|
break;
|
|
|
|
}
|
2021-01-11 16:10:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return visited;
|
|
|
|
}
|
|
|
|
|
2020-10-28 14:38:14 +01:00
|
|
|
// <editor-fold defaultstate="collapsed" desc="Primitive accessors.">
|
|
|
|
@Override
|
|
|
|
public byte readByte() {
|
|
|
|
return prepRead(Byte.BYTES).readByte();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public byte getByte(int roff) {
|
|
|
|
return prepRead(roff, Byte.BYTES).getByte(subOffset);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int readUnsignedByte() {
|
|
|
|
return prepRead(Byte.BYTES).readUnsignedByte();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public int getUnsignedByte(int roff) {
|
|
|
|
return prepRead(roff, Byte.BYTES).getUnsignedByte(subOffset);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeByte(byte value) {
|
|
|
|
prepWrite(Byte.BYTES).writeByte(value);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setByte(int woff, byte value) {
|
|
|
|
prepWrite(woff, Byte.BYTES).setByte(subOffset, value);
|
2020-10-28 14:38:14 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeUnsignedByte(int value) {
|
|
|
|
prepWrite(Byte.BYTES).writeUnsignedByte(value);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setUnsignedByte(int woff, int value) {
|
|
|
|
prepWrite(woff, Byte.BYTES).setUnsignedByte(subOffset, value);
|
2020-10-28 14:38:14 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public char readChar() {
|
|
|
|
return prepRead(2).readChar();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public char getChar(int roff) {
|
|
|
|
return prepRead(roff, 2).getChar(subOffset);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeChar(char value) {
|
|
|
|
prepWrite(2).writeChar(value);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setChar(int woff, char value) {
|
|
|
|
prepWrite(woff, 2).setChar(subOffset, value);
|
2020-10-28 14:38:14 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public short readShort() {
|
|
|
|
return prepRead(Short.BYTES).readShort();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public short getShort(int roff) {
|
|
|
|
return prepRead(roff, Short.BYTES).getShort(subOffset);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int readUnsignedShort() {
|
|
|
|
return prepRead(Short.BYTES).readShort();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public int getUnsignedShort(int roff) {
|
|
|
|
return prepRead(roff, Short.BYTES).getUnsignedShort(subOffset);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeShort(short value) {
|
|
|
|
prepWrite(Short.BYTES).writeShort(value);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setShort(int woff, short value) {
|
|
|
|
prepWrite(woff, Short.BYTES).setShort(subOffset, value);
|
2020-10-28 14:38:14 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeUnsignedShort(int value) {
|
|
|
|
prepWrite(Short.BYTES).writeUnsignedShort(value);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setUnsignedShort(int woff, int value) {
|
|
|
|
prepWrite(woff, Short.BYTES).setUnsignedShort(subOffset, value);
|
2020-10-28 14:38:14 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int readMedium() {
|
|
|
|
return prepRead(3).readMedium();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public int getMedium(int roff) {
|
|
|
|
return prepRead(roff, 3).getMedium(subOffset);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int readUnsignedMedium() {
|
|
|
|
return prepRead(3).readMedium();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public int getUnsignedMedium(int roff) {
|
|
|
|
return prepRead(roff, 3).getMedium(subOffset);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeMedium(int value) {
|
|
|
|
prepWrite(3).writeMedium(value);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setMedium(int woff, int value) {
|
|
|
|
prepWrite(woff, 3).setMedium(subOffset, value);
|
2020-10-28 14:38:14 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeUnsignedMedium(int value) {
|
|
|
|
prepWrite(3).writeUnsignedMedium(value);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setUnsignedMedium(int woff, int value) {
|
|
|
|
prepWrite(woff, 3).setUnsignedMedium(subOffset, value);
|
2020-10-28 14:38:14 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int readInt() {
|
|
|
|
return prepRead(Integer.BYTES).readInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public int getInt(int roff) {
|
|
|
|
return prepRead(roff, Integer.BYTES).getInt(subOffset);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long readUnsignedInt() {
|
|
|
|
return prepRead(Integer.BYTES).readUnsignedInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public long getUnsignedInt(int roff) {
|
|
|
|
return prepRead(roff, Integer.BYTES).getUnsignedInt(subOffset);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeInt(int value) {
|
|
|
|
prepWrite(Integer.BYTES).writeInt(value);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setInt(int woff, int value) {
|
|
|
|
prepWrite(woff, Integer.BYTES).setInt(subOffset, value);
|
2020-10-28 14:38:14 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeUnsignedInt(long value) {
|
|
|
|
prepWrite(Integer.BYTES).writeUnsignedInt(value);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setUnsignedInt(int woff, long value) {
|
|
|
|
prepWrite(woff, Integer.BYTES).setUnsignedInt(subOffset, value);
|
2020-10-28 14:38:14 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public float readFloat() {
|
|
|
|
return prepRead(Float.BYTES).readFloat();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public float getFloat(int roff) {
|
|
|
|
return prepRead(roff, Float.BYTES).getFloat(subOffset);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeFloat(float value) {
|
|
|
|
prepWrite(Float.BYTES).writeFloat(value);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setFloat(int woff, float value) {
|
|
|
|
prepWrite(woff, Float.BYTES).setFloat(subOffset, value);
|
2020-10-28 14:38:14 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long readLong() {
|
|
|
|
return prepRead(Long.BYTES).readLong();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public long getLong(int roff) {
|
|
|
|
return prepRead(roff, Long.BYTES).getLong(subOffset);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeLong(long value) {
|
|
|
|
prepWrite(Long.BYTES).writeLong(value);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setLong(int woff, long value) {
|
|
|
|
prepWrite(woff, Long.BYTES).setLong(subOffset, value);
|
2020-10-28 14:38:14 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public double readDouble() {
|
|
|
|
return prepRead(Double.BYTES).readDouble();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public double getDouble(int roff) {
|
|
|
|
return prepRead(roff, Double.BYTES).getDouble(subOffset);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeDouble(double value) {
|
|
|
|
prepWrite(Double.BYTES).writeDouble(value);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setDouble(int woff, double value) {
|
|
|
|
prepWrite(woff, Double.BYTES).setDouble(subOffset, value);
|
2020-10-28 14:38:14 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
// </editor-fold>
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected Owned<CompositeBuf> prepareSend() {
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
Send<Buf>[] sends = new Send[bufs.length];
|
|
|
|
try {
|
|
|
|
for (int i = 0; i < bufs.length; i++) {
|
|
|
|
sends[i] = bufs[i].send();
|
|
|
|
}
|
|
|
|
} catch (Throwable throwable) {
|
|
|
|
// Repair our bufs array.
|
|
|
|
for (int i = 0; i < sends.length; i++) {
|
|
|
|
if (sends[i] != null) {
|
|
|
|
try {
|
|
|
|
bufs[i] = sends[i].receive();
|
|
|
|
} catch (Exception e) {
|
|
|
|
throwable.addSuppressed(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw throwable;
|
|
|
|
}
|
2020-12-14 14:07:11 +01:00
|
|
|
makeInaccessible();
|
2020-10-28 14:38:14 +01:00
|
|
|
return new Owned<CompositeBuf>() {
|
|
|
|
@Override
|
|
|
|
public CompositeBuf transferOwnership(Drop<CompositeBuf> drop) {
|
|
|
|
Buf[] received = new Buf[sends.length];
|
|
|
|
for (int i = 0; i < sends.length; i++) {
|
|
|
|
received[i] = sends[i].receive();
|
|
|
|
}
|
2020-11-16 18:00:32 +01:00
|
|
|
var composite = new CompositeBuf(allocator, true, received, drop);
|
2021-01-05 16:53:21 +01:00
|
|
|
composite.readOnly = readOnly;
|
2020-12-10 12:51:18 +01:00
|
|
|
drop.attach(composite);
|
2020-10-28 14:38:14 +01:00
|
|
|
return composite;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-12-14 14:07:11 +01:00
|
|
|
void makeInaccessible() {
|
|
|
|
capacity = 0;
|
|
|
|
roff = 0;
|
|
|
|
woff = 0;
|
|
|
|
closed = true;
|
|
|
|
}
|
|
|
|
|
2020-10-30 14:21:20 +01:00
|
|
|
@Override
|
|
|
|
protected IllegalStateException notSendableException() {
|
|
|
|
if (!isSendable) {
|
|
|
|
return new IllegalStateException(
|
|
|
|
"Cannot send() this buffer. This buffer might be a slice of another buffer.");
|
|
|
|
}
|
|
|
|
return super.notSendableException();
|
|
|
|
}
|
2020-10-28 14:38:14 +01:00
|
|
|
|
2020-10-30 14:21:20 +01:00
|
|
|
@Override
|
2020-11-16 18:00:32 +01:00
|
|
|
public boolean isOwned() {
|
|
|
|
return isSendable && super.isOwned() && allConstituentsAreOwned();
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean allConstituentsAreOwned() {
|
|
|
|
boolean result = true;
|
|
|
|
for (Buf buf : bufs) {
|
|
|
|
result &= buf.isOwned();
|
|
|
|
}
|
|
|
|
return result;
|
2020-10-30 14:21:20 +01:00
|
|
|
}
|
2020-10-28 14:38:14 +01:00
|
|
|
|
|
|
|
long readPassThrough() {
|
|
|
|
var buf = choosePassThroughBuffer(subOffset++);
|
|
|
|
assert buf != tornBufAccessors: "Recursive call to torn buffer.";
|
|
|
|
return buf.readUnsignedByte();
|
|
|
|
}
|
|
|
|
|
|
|
|
void writePassThrough(int value) {
|
|
|
|
var buf = choosePassThroughBuffer(subOffset++);
|
|
|
|
assert buf != tornBufAccessors: "Recursive call to torn buffer.";
|
|
|
|
buf.writeUnsignedByte(value);
|
|
|
|
}
|
|
|
|
|
2020-11-11 23:00:08 +01:00
|
|
|
long getPassThrough(int roff) {
|
2020-10-28 14:38:14 +01:00
|
|
|
var buf = chooseBuffer(roff, 1);
|
|
|
|
assert buf != tornBufAccessors: "Recursive call to torn buffer.";
|
2020-11-11 23:00:08 +01:00
|
|
|
return buf.getUnsignedByte(subOffset);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
2020-11-11 23:00:08 +01:00
|
|
|
void setPassThrough(int woff, int value) {
|
2020-10-28 14:38:14 +01:00
|
|
|
var buf = chooseBuffer(woff, 1);
|
|
|
|
assert buf != tornBufAccessors: "Recursive call to torn buffer.";
|
2020-11-11 23:00:08 +01:00
|
|
|
buf.setUnsignedByte(subOffset, value);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private BufAccessors prepRead(int size) {
|
|
|
|
var buf = prepRead(roff, size);
|
|
|
|
roff += size;
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
private BufAccessors prepRead(int index, int size) {
|
|
|
|
checkReadBounds(index, size);
|
|
|
|
return chooseBuffer(index, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void checkReadBounds(int index, int size) {
|
|
|
|
if (index < 0 || woff < index + size) {
|
2021-01-05 16:53:21 +01:00
|
|
|
throw indexOutOfBounds(index, false);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private BufAccessors prepWrite(int size) {
|
|
|
|
var buf = prepWrite(woff, size);
|
|
|
|
woff += size;
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
private BufAccessors prepWrite(int index, int size) {
|
|
|
|
checkWriteBounds(index, size);
|
|
|
|
return chooseBuffer(index, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void checkWriteBounds(int index, int size) {
|
|
|
|
if (index < 0 || capacity < index + size) {
|
2021-01-05 16:53:21 +01:00
|
|
|
throw indexOutOfBounds(index, true);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-05 16:53:21 +01:00
|
|
|
private RuntimeException indexOutOfBounds(int index, boolean write) {
|
2020-12-14 14:07:11 +01:00
|
|
|
if (closed) {
|
2021-01-05 16:53:21 +01:00
|
|
|
return bufferIsClosed();
|
|
|
|
}
|
|
|
|
if (write && readOnly) {
|
|
|
|
return bufferIsReadOnly();
|
2020-12-14 14:07:11 +01:00
|
|
|
}
|
2020-10-28 14:38:14 +01:00
|
|
|
return new IndexOutOfBoundsException(
|
|
|
|
"Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " +
|
|
|
|
(capacity - 1) + "].");
|
|
|
|
}
|
|
|
|
|
2021-01-05 16:53:21 +01:00
|
|
|
private static IllegalStateException bufferIsClosed() {
|
|
|
|
return new IllegalStateException("This buffer is closed.");
|
|
|
|
}
|
|
|
|
|
|
|
|
private static IllegalStateException bufferIsReadOnly() {
|
|
|
|
return new IllegalStateException("This buffer is read-only.");
|
|
|
|
}
|
|
|
|
|
2020-10-28 14:38:14 +01:00
|
|
|
private BufAccessors chooseBuffer(int index, int size) {
|
|
|
|
int i = searchOffsets(index);
|
|
|
|
if (i == bufs.length) {
|
|
|
|
// This happens when the read/write offsets are parked 1 byte beyond the end of the buffer.
|
|
|
|
// In that case it should not matter what buffer is returned, because it shouldn't be used anyway.
|
|
|
|
return null;
|
|
|
|
}
|
2020-11-05 15:15:34 +01:00
|
|
|
int off = index - offsets[i];
|
2020-10-28 14:38:14 +01:00
|
|
|
Buf candidate = bufs[i];
|
|
|
|
if (off + size <= candidate.capacity()) {
|
|
|
|
subOffset = off;
|
|
|
|
return candidate;
|
|
|
|
}
|
|
|
|
subOffset = index;
|
|
|
|
return tornBufAccessors;
|
|
|
|
}
|
|
|
|
|
|
|
|
private BufAccessors choosePassThroughBuffer(int index) {
|
|
|
|
int i = searchOffsets(index);
|
|
|
|
return bufs[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
private int searchOffsets(int index) {
|
|
|
|
int i = Arrays.binarySearch(offsets, index);
|
2020-11-17 15:25:26 +01:00
|
|
|
return i < 0? -(i + 2) : i;
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
2020-11-05 15:15:34 +01:00
|
|
|
// <editor-fold defaultstate="collapsed" desc="Torn buffer access.">
|
2020-10-28 14:38:14 +01:00
|
|
|
private static final class TornBufAccessors implements BufAccessors {
|
|
|
|
private final CompositeBuf buf;
|
|
|
|
|
|
|
|
private TornBufAccessors(CompositeBuf buf) {
|
|
|
|
this.buf = buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public byte readByte() {
|
|
|
|
throw new AssertionError("Method should not be used.");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public byte getByte(int roff) {
|
2020-10-28 14:38:14 +01:00
|
|
|
throw new AssertionError("Method should not be used.");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int readUnsignedByte() {
|
|
|
|
throw new AssertionError("Method should not be used.");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public int getUnsignedByte(int roff) {
|
2020-10-28 14:38:14 +01:00
|
|
|
throw new AssertionError("Method should not be used.");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeByte(byte value) {
|
|
|
|
throw new AssertionError("Method should not be used.");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setByte(int woff, byte value) {
|
2020-10-28 14:38:14 +01:00
|
|
|
throw new AssertionError("Method should not be used.");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeUnsignedByte(int value) {
|
|
|
|
throw new AssertionError("Method should not be used.");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setUnsignedByte(int woff, int value) {
|
2020-10-28 14:38:14 +01:00
|
|
|
throw new AssertionError("Method should not be used.");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public char readChar() {
|
|
|
|
if (bigEndian()) {
|
|
|
|
return (char) (read() << 8 | read());
|
|
|
|
} else {
|
|
|
|
return (char) (read() | read() << 8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public char getChar(int roff) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
return (char) (read(roff) << 8 | read(roff + 1));
|
|
|
|
} else {
|
|
|
|
return (char) (read(roff) | read(roff + 1) << 8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeChar(char value) {
|
|
|
|
if (bigEndian()) {
|
|
|
|
write(value >>> 8);
|
|
|
|
write(value & 0xFF);
|
|
|
|
} else {
|
|
|
|
write(value & 0xFF);
|
|
|
|
write(value >>> 8);
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setChar(int woff, char value) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
write(woff, value >>> 8);
|
|
|
|
write(woff + 1, value & 0xFF);
|
|
|
|
} else {
|
|
|
|
write(woff, value & 0xFF);
|
|
|
|
write(woff + 1, value >>> 8);
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public short readShort() {
|
|
|
|
if (bigEndian()) {
|
|
|
|
return (short) (read() << 8 | read());
|
|
|
|
} else {
|
|
|
|
return (short) (read() | read() << 8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public short getShort(int roff) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
return (short) (read(roff) << 8 | read(roff + 1));
|
|
|
|
} else {
|
|
|
|
return (short) (read(roff) | read(roff + 1) << 8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int readUnsignedShort() {
|
|
|
|
if (bigEndian()) {
|
|
|
|
return (int) (read() << 8 | read()) & 0xFFFF;
|
|
|
|
} else {
|
|
|
|
return (int) (read() | read() << 8) & 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public int getUnsignedShort(int roff) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
return (int) (read(roff) << 8 | read(roff + 1)) & 0xFFFF;
|
|
|
|
} else {
|
|
|
|
return (int) (read(roff) | read(roff + 1) << 8) & 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeShort(short value) {
|
|
|
|
if (bigEndian()) {
|
|
|
|
write(value >>> 8);
|
|
|
|
write(value & 0xFF);
|
|
|
|
} else {
|
|
|
|
write(value & 0xFF);
|
|
|
|
write(value >>> 8);
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setShort(int woff, short value) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
write(woff, value >>> 8);
|
|
|
|
write(woff + 1, value & 0xFF);
|
|
|
|
} else {
|
|
|
|
write(woff, value & 0xFF);
|
|
|
|
write(woff + 1, value >>> 8);
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeUnsignedShort(int value) {
|
|
|
|
if (bigEndian()) {
|
|
|
|
write(value >>> 8);
|
|
|
|
write(value & 0xFF);
|
|
|
|
} else {
|
|
|
|
write(value & 0xFF);
|
|
|
|
write(value >>> 8);
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setUnsignedShort(int woff, int value) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
write(woff, value >>> 8);
|
|
|
|
write(woff + 1, value & 0xFF);
|
|
|
|
} else {
|
|
|
|
write(woff, value & 0xFF);
|
|
|
|
write(woff + 1, value >>> 8);
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int readMedium() {
|
|
|
|
if (bigEndian()) {
|
|
|
|
return (int) (read() << 16 | read() << 8 | read());
|
|
|
|
} else {
|
|
|
|
return (int) (read() | read() << 8 | read() << 16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public int getMedium(int roff) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
return (int) (read(roff) << 16 | read(roff + 1) << 8 | read(roff + 2));
|
|
|
|
} else {
|
|
|
|
return (int) (read(roff) | read(roff + 1) << 8 | read(roff + 2) << 16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int readUnsignedMedium() {
|
|
|
|
if (bigEndian()) {
|
|
|
|
return (int) (read() << 16 | read() << 8 | read()) & 0xFFFFFF;
|
|
|
|
} else {
|
|
|
|
return (int) (read() | read() << 8 | read() << 16) & 0xFFFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public int getUnsignedMedium(int roff) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
return (int) (read(roff) << 16 | read(roff + 1) << 8 | read(roff + 2)) & 0xFFFFFF;
|
|
|
|
} else {
|
|
|
|
return (int) (read(roff) | read(roff + 1) << 8 | read(roff + 2) << 16) & 0xFFFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeMedium(int value) {
|
|
|
|
if (bigEndian()) {
|
|
|
|
write(value >>> 16);
|
|
|
|
write(value >>> 8 & 0xFF);
|
|
|
|
write(value & 0xFF);
|
|
|
|
} else {
|
|
|
|
write(value & 0xFF);
|
|
|
|
write(value >>> 8 & 0xFF);
|
|
|
|
write(value >>> 16);
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setMedium(int woff, int value) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
write(woff, value >>> 16);
|
|
|
|
write(woff + 1, value >>> 8 & 0xFF);
|
|
|
|
write(woff + 2, value & 0xFF);
|
|
|
|
} else {
|
|
|
|
write(woff, value & 0xFF);
|
|
|
|
write(woff + 1, value >>> 8 & 0xFF);
|
|
|
|
write(woff + 2, value >>> 16);
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeUnsignedMedium(int value) {
|
|
|
|
if (bigEndian()) {
|
|
|
|
write(value >>> 16);
|
|
|
|
write(value >>> 8 & 0xFF);
|
|
|
|
write(value & 0xFF);
|
|
|
|
} else {
|
|
|
|
write(value & 0xFF);
|
|
|
|
write(value >>> 8 & 0xFF);
|
|
|
|
write(value >>> 16);
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setUnsignedMedium(int woff, int value) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
write(woff, value >>> 16);
|
|
|
|
write(woff + 1, value >>> 8 & 0xFF);
|
|
|
|
write(woff + 2, value & 0xFF);
|
|
|
|
} else {
|
|
|
|
write(woff, value & 0xFF);
|
|
|
|
write(woff + 1, value >>> 8 & 0xFF);
|
|
|
|
write(woff + 2, value >>> 16);
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int readInt() {
|
|
|
|
if (bigEndian()) {
|
|
|
|
return (int) (read() << 24 | read() << 16 | read() << 8 | read());
|
|
|
|
} else {
|
|
|
|
return (int) (read() | read() << 8 | read() << 16 | read() << 24);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public int getInt(int roff) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
return (int) (read(roff) << 24 | read(roff + 1) << 16 | read(roff + 2) << 8 | read(roff + 3));
|
|
|
|
} else {
|
|
|
|
return (int) (read(roff) | read(roff + 1) << 8 | read(roff + 2) << 16 | read(roff + 3) << 24);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long readUnsignedInt() {
|
|
|
|
if (bigEndian()) {
|
|
|
|
return (read() << 24 | read() << 16 | read() << 8 | read()) & 0xFFFFFFFFL;
|
|
|
|
} else {
|
|
|
|
return (read() | read() << 8 | read() << 16 | read() << 24) & 0xFFFFFFFFL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public long getUnsignedInt(int roff) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
return (read(roff) << 24 | read(roff + 1) << 16 | read(roff + 2) << 8 | read(roff + 3)) & 0xFFFFFFFFL;
|
|
|
|
} else {
|
|
|
|
return (read(roff) | read(roff + 1) << 8 | read(roff + 2) << 16 | read(roff + 3) << 24) & 0xFFFFFFFFL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeInt(int value) {
|
|
|
|
if (bigEndian()) {
|
|
|
|
write(value >>> 24);
|
|
|
|
write(value >>> 16 & 0xFF);
|
|
|
|
write(value >>> 8 & 0xFF);
|
|
|
|
write(value & 0xFF);
|
|
|
|
} else {
|
|
|
|
write(value & 0xFF);
|
|
|
|
write(value >>> 8 & 0xFF);
|
|
|
|
write(value >>> 16 & 0xFF);
|
|
|
|
write(value >>> 24);
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setInt(int woff, int value) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
write(woff, value >>> 24);
|
|
|
|
write(woff + 1, value >>> 16 & 0xFF);
|
|
|
|
write(woff + 2, value >>> 8 & 0xFF);
|
|
|
|
write(woff + 3, value & 0xFF);
|
|
|
|
} else {
|
|
|
|
write(woff, value & 0xFF);
|
|
|
|
write(woff + 1, value >>> 8 & 0xFF);
|
|
|
|
write(woff + 2, value >>> 16 & 0xFF);
|
|
|
|
write(woff + 3, value >>> 24);
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeUnsignedInt(long value) {
|
|
|
|
if (bigEndian()) {
|
|
|
|
write((int) (value >>> 24));
|
|
|
|
write((int) (value >>> 16 & 0xFF));
|
|
|
|
write((int) (value >>> 8 & 0xFF));
|
|
|
|
write((int) (value & 0xFF));
|
|
|
|
} else {
|
|
|
|
write((int) (value & 0xFF));
|
|
|
|
write((int) (value >>> 8 & 0xFF));
|
|
|
|
write((int) (value >>> 16 & 0xFF));
|
|
|
|
write((int) (value >>> 24));
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setUnsignedInt(int woff, long value) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
write(woff, (int) (value >>> 24));
|
|
|
|
write(woff + 1, (int) (value >>> 16 & 0xFF));
|
|
|
|
write(woff + 2, (int) (value >>> 8 & 0xFF));
|
|
|
|
write(woff + 3, (int) (value & 0xFF));
|
|
|
|
} else {
|
|
|
|
write(woff, (int) (value & 0xFF));
|
|
|
|
write(woff + 1, (int) (value >>> 8 & 0xFF));
|
|
|
|
write(woff + 2, (int) (value >>> 16 & 0xFF));
|
|
|
|
write(woff + 3, (int) (value >>> 24));
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public float readFloat() {
|
|
|
|
return Float.intBitsToFloat(readInt());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public float getFloat(int roff) {
|
|
|
|
return Float.intBitsToFloat(getInt(roff));
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeFloat(float value) {
|
|
|
|
return writeUnsignedInt(Float.floatToRawIntBits(value));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setFloat(int woff, float value) {
|
|
|
|
return setUnsignedInt(woff, Float.floatToRawIntBits(value));
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long readLong() {
|
|
|
|
if (bigEndian()) {
|
|
|
|
return read() << 56 | read() << 48 | read() << 40 | read() << 32 |
|
|
|
|
read() << 24 | read() << 16 | read() << 8 | read();
|
|
|
|
} else {
|
|
|
|
return read() | read() << 8 | read() << 16 | read() << 24 |
|
|
|
|
read() << 32 | read() << 40 | read() << 48 | read() << 56;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public long getLong(int roff) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
return read(roff) << 56 | read(roff + 1) << 48 | read(roff + 2) << 40 | read(roff + 3) << 32 |
|
|
|
|
read(roff + 4) << 24 | read(roff + 5) << 16 | read(roff + 6) << 8 | read(roff + 7);
|
|
|
|
} else {
|
|
|
|
return read(roff) | read(roff + 1) << 8 | read(roff + 2) << 16 | read(roff + 3) << 24 |
|
|
|
|
read(roff + 4) << 32 | read(roff + 5) << 40 | read(roff + 6) << 48 | read(roff + 7) << 56;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeLong(long value) {
|
|
|
|
if (bigEndian()) {
|
|
|
|
write((int) (value >>> 56));
|
|
|
|
write((int) (value >>> 48 & 0xFF));
|
|
|
|
write((int) (value >>> 40 & 0xFF));
|
|
|
|
write((int) (value >>> 32 & 0xFF));
|
|
|
|
write((int) (value >>> 24 & 0xFF));
|
|
|
|
write((int) (value >>> 16 & 0xFF));
|
|
|
|
write((int) (value >>> 8 & 0xFF));
|
|
|
|
write((int) (value & 0xFF));
|
|
|
|
} else {
|
|
|
|
write((int) (value & 0xFF));
|
|
|
|
write((int) (value >>> 8 & 0xFF));
|
|
|
|
write((int) (value >>> 16 & 0xFF));
|
|
|
|
write((int) (value >>> 24));
|
|
|
|
write((int) (value >>> 32));
|
|
|
|
write((int) (value >>> 40));
|
|
|
|
write((int) (value >>> 48));
|
|
|
|
write((int) (value >>> 56));
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setLong(int woff, long value) {
|
2020-10-28 14:38:14 +01:00
|
|
|
if (bigEndian()) {
|
|
|
|
write(woff, (int) (value >>> 56));
|
|
|
|
write(woff + 1, (int) (value >>> 48 & 0xFF));
|
|
|
|
write(woff + 2, (int) (value >>> 40 & 0xFF));
|
|
|
|
write(woff + 3, (int) (value >>> 32 & 0xFF));
|
|
|
|
write(woff + 4, (int) (value >>> 24 & 0xFF));
|
|
|
|
write(woff + 5, (int) (value >>> 16 & 0xFF));
|
|
|
|
write(woff + 6, (int) (value >>> 8 & 0xFF));
|
|
|
|
write(woff + 7, (int) (value & 0xFF));
|
|
|
|
} else {
|
|
|
|
write(woff, (int) (value & 0xFF));
|
|
|
|
write(woff + 1, (int) (value >>> 8 & 0xFF));
|
|
|
|
write(woff + 2, (int) (value >>> 16 & 0xFF));
|
|
|
|
write(woff + 3, (int) (value >>> 24));
|
|
|
|
write(woff + 4, (int) (value >>> 32));
|
|
|
|
write(woff + 5, (int) (value >>> 40));
|
|
|
|
write(woff + 6, (int) (value >>> 48));
|
|
|
|
write(woff + 7, (int) (value >>> 56));
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public double readDouble() {
|
|
|
|
return Double.longBitsToDouble(readLong());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public double getDouble(int roff) {
|
|
|
|
return Double.longBitsToDouble(getLong(roff));
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Buf writeDouble(double value) {
|
|
|
|
return writeLong(Double.doubleToRawLongBits(value));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-11-11 23:00:08 +01:00
|
|
|
public Buf setDouble(int woff, double value) {
|
|
|
|
return setLong(woff, Double.doubleToRawLongBits(value));
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private boolean bigEndian() {
|
|
|
|
return buf.order() == ByteOrder.BIG_ENDIAN;
|
|
|
|
}
|
|
|
|
|
|
|
|
private long read() {
|
|
|
|
return buf.readPassThrough();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void write(int value) {
|
|
|
|
buf.writePassThrough(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
private long read(int roff) {
|
2020-11-11 23:00:08 +01:00
|
|
|
return buf.getPassThrough(roff);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void write(int woff, int value) {
|
2020-11-11 23:00:08 +01:00
|
|
|
buf.setPassThrough(woff, value);
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|
|
|
|
}
|
2020-11-05 15:15:34 +01:00
|
|
|
// </editor-fold>
|
2020-10-28 14:38:14 +01:00
|
|
|
}
|