/*
* Copyright 2016-2020 chronicle.software
*
* https://chronicle.software
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.openhft.chronicle.bytes;
import net.openhft.chronicle.bytes.util.DecoratedBufferOverflowException;
import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.Memory;
import net.openhft.chronicle.core.OS;
import net.openhft.chronicle.core.UnsafeMemory;
import net.openhft.chronicle.core.io.Closeable;
import net.openhft.chronicle.core.io.IORuntimeException;
import net.openhft.chronicle.core.io.ReferenceOwner;
import net.openhft.chronicle.core.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import static net.openhft.chronicle.core.util.StringUtils.*;
/**
* Bytes to wrap memory mapped data.
*
* NOTE These Bytes are single Threaded as are all Bytes.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class MappedBytes extends AbstractBytes implements Closeable {
private static final boolean TRACE = Jvm.getBoolean("trace.mapped.bytes");
private MappedBytesStore bytesStore;
private final MappedFile mappedFile;
private final boolean backingFileIsReadOnly;
private boolean closed;
// assume the mapped file is reserved already.
protected MappedBytes( final MappedFile mappedFile) throws IllegalStateException {
this(mappedFile, "");
}
protected MappedBytes( final MappedFile mappedFile, final String name) throws IllegalStateException {
super(NoBytesStore.noBytesStore(),
NoBytesStore.noBytesStore().writePosition(),
NoBytesStore.noBytesStore().writeLimit(),
name);
assert mappedFile != null;
this.mappedFile = mappedFile;
mappedFile.reserve(this);
this.backingFileIsReadOnly = !mappedFile.file().canWrite();
assert !mappedFile.isClosed();
clear();
}
public static MappedBytes mappedBytes( final String filename, final long chunkSize)
throws FileNotFoundException, IllegalStateException {
return mappedBytes(new File(filename), chunkSize);
}
public static MappedBytes mappedBytes( final File file, final long chunkSize)
throws FileNotFoundException, IllegalStateException {
return mappedBytes(file, chunkSize, OS.pageSize());
}
public static MappedBytes mappedBytes( final File file, final long chunkSize, final long overlapSize)
throws FileNotFoundException, IllegalStateException {
final MappedFile rw = MappedFile.of(file, chunkSize, overlapSize, false);
try {
return mappedBytes(rw);
} finally {
rw.release(INIT);
}
}
public static MappedBytes mappedBytes( final File file,
final long chunkSize,
final long overlapSize,
final boolean readOnly) throws FileNotFoundException,
IllegalStateException {
final MappedFile rw = MappedFile.of(file, chunkSize, overlapSize, readOnly);
try {
return mappedBytes(rw);
} finally {
rw.release(INIT);
}
}
public static MappedBytes mappedBytes( final MappedFile rw) {
return new MappedBytes(rw);
}
public static MappedBytes readOnly( final File file) throws FileNotFoundException {
MappedFile mappedFile = MappedFile.readOnly(file);
try {
return new MappedBytes(mappedFile);
} finally {
mappedFile.release(INIT);
}
}
@Override
protected void bytesStore(BytesStore bytesStore) {
super.bytesStore(bytesStore);
if (bytesStore instanceof MappedBytesStore)
this.bytesStore = (MappedBytesStore) bytesStore;
else
this.bytesStore = null;
}
@Override
public String toString() {
if (!TRACE)
return super.toString();
return "MappedBytes{" + "\n" +
"refCount=" + refCount() + ",\n" +
"mappedFile=" + mappedFile.file().getAbsolutePath() + ",\n" +
"mappedFileRefCount=" + mappedFile.refCount() + ",\n" +
"mappedFileIsClosed=" + mappedFile.isClosed() + ",\n" +
"mappedFileRafIsClosed=" + Jvm.getValue(mappedFile.raf(), "closed") + ",\n" +
"mappedFileRafChannelIsClosed=" + !mappedFile.raf().getChannel().isOpen() + ",\n" +
"isClosed=" + isClosed() +
'}';
}
public MappedBytes write( final byte[] bytes,
final int offset,
final int length) {
throwExceptionIfClosed();
write(writePosition, bytes, offset, length);
writePosition += Math.min(length, bytes.length - offset);
return this;
}
public MappedBytes write(final long offsetInRDO,
final byte[] bytes,
int offset,
final int length) {
throwExceptionIfClosed();
long wp = offsetInRDO;
if ((length + offset) > bytes.length)
throw new ArrayIndexOutOfBoundsException("bytes.length=" + bytes.length + ", " + "length=" + length + ", offset=" + offset);
if (length > writeRemaining())
throw new DecoratedBufferOverflowException(
String.format("write failed. Length: %d > writeRemaining: %d", length, writeRemaining()));
int remaining = length;
acquireNextByteStore(wp, false);
while (remaining > 0) {
long safeCopySize = copySize(wp);
if (safeCopySize + mappedFile.overlapSize() >= remaining) {
bytesStore.write(wp, bytes, offset, remaining);
return this;
}
bytesStore.write(wp, bytes, offset, (int) safeCopySize);
offset += safeCopySize;
wp += safeCopySize;
remaining -= safeCopySize;
// move to the next chunk
acquireNextByteStore0(wp, false);
}
return this;
}
public MappedBytes write(final long writeOffset,
final RandomDataInput bytes,
long readOffset,
final long length)
throws BufferOverflowException, BufferUnderflowException {
throwExceptionIfClosed();
long wp = writeOffset;
if (length > writeRemaining())
throw new DecoratedBufferOverflowException(
String.format("write failed. Length: %d > writeRemaining: %d", length, writeRemaining()));
long remaining = length;
acquireNextByteStore(wp, false);
while (remaining > 0) {
long safeCopySize = copySize(wp);
if (safeCopySize + mappedFile.overlapSize() >= remaining) {
bytesStore.write(wp, bytes, readOffset, remaining);
return this;
}
bytesStore.write(wp, bytes, readOffset, safeCopySize);
readOffset += safeCopySize;
wp += safeCopySize;
remaining -= safeCopySize;
// move to the next chunk
acquireNextByteStore0(wp, false);
}
return this;
}
@Override
public MappedBytes write( final RandomDataInput bytes)
throws BufferOverflowException {
throwExceptionIfClosed();
assert bytes != this : "you should not write to yourself !";
final long remaining = bytes.readRemaining();
write(writePosition, bytes);
writePosition += remaining;
return this;
}
public MappedBytes write(final long offsetInRDO, final RandomDataInput bytes)
throws BufferOverflowException {
throwExceptionIfClosed();
write(offsetInRDO, bytes, bytes.readPosition(), bytes.readRemaining());
return this;
}
private long copySize(final long writePosition) {
long size = mappedFile.chunkSize();
return size - writePosition % size;
}
public void setNewChunkListener(final NewChunkListener listener) {
mappedFile.setNewChunkListener(listener);
}
public MappedFile mappedFile() {
return mappedFile;
}
@Override
public BytesStore, Void> copy() {
return NativeBytes.copyOf(this);
}
@Override
public long capacity() {
return mappedFile.capacity();
}
@Override
public Bytes readLimitToCapacity() throws BufferUnderflowException {
this.writePosition = mappedFile.capacity();
return this;
}
@Override
public long realCapacity() {
throwExceptionIfClosed();
try {
return mappedFile.actualSize();
} catch (IORuntimeException e) {
Jvm.warn().on(getClass(), "Unable to obtain the real size for " + mappedFile.file(), e);
return 0;
}
}
@Override
public Bytes readPositionRemaining(final long position, final long remaining) throws BufferUnderflowException {
// throwExceptionIfClosed();
final long limit = position + remaining;
acquireNextByteStore(position, true);
if (writeLimit < limit)
writeLimit(limit);
boolean debug = false;
assert debug = true;
if (debug)
readLimit(limit);
else
uncheckedWritePosition(limit);
return readPosition(position);
}
@Override
public Bytes readPosition(final long position) throws BufferUnderflowException {
// throwExceptionIfClosed();
if (bytesStore != null && bytesStore.inside(position)) {
return super.readPosition(position);
} else {
acquireNextByteStore0(position, true);
return this;
}
}
@Override
public long addressForRead(final long offset) throws BufferUnderflowException {
// throwExceptionIfClosed();
if (bytesStore == null || !bytesStore.inside(offset))
acquireNextByteStore0(offset, true);
return bytesStore.addressForRead(offset);
}
@Override
public long addressForRead(final long offset, final int buffer) throws UnsupportedOperationException, BufferUnderflowException {
// throwExceptionIfClosed();
if (bytesStore == null || !bytesStore.inside(offset, buffer))
acquireNextByteStore0(offset, true);
return bytesStore.addressForRead(offset);
}
@Override
public long addressForWrite(final long offset) throws UnsupportedOperationException, BufferOverflowException {
// throwExceptionIfClosed();
if (bytesStore == null || !bytesStore.inside(offset))
acquireNextByteStore0(offset, true);
return bytesStore.addressForWrite(offset);
}
@Override
protected void readCheckOffset(final long offset,
final long adding,
final boolean given) throws BufferUnderflowException {
final long check = adding >= 0 ? offset : offset + adding;
//noinspection StatementWithEmptyBody
if (bytesStore != null && bytesStore.inside(check, adding)) {
// nothing.
} else {
acquireNextByteStore0(offset, false);
}
super.readCheckOffset(offset, adding, given);
}
@Override
public String read8bit() throws IORuntimeException, BufferUnderflowException {
// throwExceptionIfClosed();
return BytesInternal.read8bit(this);
}
@Override
protected void writeCheckOffset(final long offset, final long adding) throws BufferOverflowException {
if (offset < 0 || offset > mappedFile.capacity() - adding)
throw writeBufferOverflowException(offset);
if (bytesStore == null || !bytesStore.inside(offset, Math.toIntExact(adding))) {
acquireNextByteStore0(offset, false);
}
// super.writeCheckOffset(offset, adding);
}
@Override
public void ensureCapacity(final long size) throws IllegalArgumentException {
throwExceptionIfClosed();
if (bytesStore == null || !bytesStore.inside(writePosition, Math.toIntExact(size))) {
acquireNextByteStore0(writePosition, false);
}
}
private BufferOverflowException writeBufferOverflowException(final long offset) {
BufferOverflowException exception = new BufferOverflowException();
exception.initCause(new IllegalArgumentException("Offset out of bound " + offset));
return exception;
}
private void acquireNextByteStore(final long offset, final boolean set) throws BufferOverflowException, IllegalStateException {
// if in the same chunk, can continue even if closed, but not released.
if (bytesStore != null && bytesStore.inside(offset))
return;
// not allowed if closed.
throwExceptionIfReleased();
acquireNextByteStore0(offset, set);
}
// DON'T call this directly.
// TODO Check whether we need synchronized; original comment; require protection from concurrent mutation to bytesStore field
private synchronized void acquireNextByteStore0(final long offset, final boolean set) {
throwExceptionIfClosed();
final BytesStore oldBS = this.bytesStore;
try {
final MappedBytesStore newBS = mappedFile.acquireByteStore(this, offset, oldBS);
if (newBS != oldBS) {
this.bytesStore(newBS);
if (oldBS != null)
oldBS.release(this);
}
assert this.bytesStore.reservedBy(this);
} catch ( IOException | IllegalArgumentException e) {
final BufferOverflowException boe = new BufferOverflowException();
boe.initCause(e);
throw boe;
}
if (set) {
if (writeLimit() < readPosition)
writeLimit(readPosition);
if (readLimit() < readPosition)
readLimit(readPosition);
readPosition = offset;
}
}
@Override
public Bytes readSkip(final long bytesToSkip)
throws BufferUnderflowException {
// called often so skip this check for performance
// throwExceptionIfClosed();
if (readPosition + bytesToSkip > readLimit()) throw new BufferUnderflowException();
long check = bytesToSkip >= 0 ? this.readPosition : this.readPosition + bytesToSkip;
if (bytesStore == null ||
bytesToSkip != (int) bytesToSkip ||
!bytesStore.inside(readPosition, (int) bytesToSkip)) {
acquireNextByteStore0(check, false);
}
this.readPosition += bytesToSkip;
return this;
}
@Override
public MappedBytesStore bytesStore() {
// throwExceptionIfClosed();
return (MappedBytesStore) super.bytesStore();
}
@Override
public long start() {
// throwExceptionIfClosed();
return 0L;
}
@Override
public Bytes writePosition(final long position) throws BufferOverflowException {
// throwExceptionIfClosed();
if (position > writeLimit)
throw new BufferOverflowException();
if (position < 0L)
throw new BufferUnderflowException();
if (position < readPosition)
this.readPosition = position;
this.writePosition = position;
return this;
}
@Override
public Bytes clear() {
// typically only used at the start of an operation so reject if closed.
throwExceptionIfClosed();
long start = 0L;
readPosition = start;
this.writePosition = start;
writeLimit = mappedFile.capacity();
return this;
}
@Override
public Bytes writeByte(final byte i8) throws BufferOverflowException {
throwExceptionIfClosed();
final long oldPosition = writePosition;
if (writePosition < 0 || writePosition > capacity() - (long) 1)
throw writeBufferOverflowException(writePosition);
if (bytesStore == null || !bytesStore.inside(writePosition, 1)) {
// already determined we need it
acquireNextByteStore0(writePosition, false);
}
this.writePosition = writePosition + (long) 1;
bytesStore.writeByte(oldPosition, i8);
return this;
}
@Override
protected void performRelease() throws IllegalStateException {
super.performRelease();
mappedFile.release(this);
}
@Override
protected boolean performReleaseInBackground() {
return true;
}
@Override
public boolean isElastic() {
return true;
}
public boolean isBackingFileReadOnly() {
// throwExceptionIfClosed();
return backingFileIsReadOnly;
}
@Override
public Bytes write( final RandomDataInput bytes,
final long offset,
final long length) throws BufferUnderflowException, BufferOverflowException {
throwExceptionIfClosed();
if (bytes instanceof BytesStore)
write((BytesStore) bytes, offset, length);
else if (length == 8)
writeLong(bytes.readLong(offset));
else if (length > 0)
BytesInternal.writeFully(bytes, offset, length, this);
return this;
}
@Override
public Bytes write( final BytesStore bytes,
final long offset,
final long length)
throws BufferUnderflowException, BufferOverflowException {
throwExceptionIfClosed();
if (length == 8) {
writeLong(bytes.readLong(offset));
} else if (length > 0) {
if (bytes.isDirectMemory()) {
// need to check this to pull in the right bytesStore()
long fromAddress = bytes.addressForRead(offset);
if (length <= bytes.bytesStore().realCapacity() - offset) {
this.acquireNextByteStore(writePosition(), false);
// can we do a direct copy of raw memory?
if (bytesStore.realCapacity() - writePosition >= length) {
rawCopy(length, fromAddress);
return this;
}
}
}
BytesInternal.writeFully(bytes, offset, length, this);
}
return this;
}
void rawCopy(final long length, final long fromAddress)
throws BufferOverflowException, BufferUnderflowException {
this.throwExceptionIfReleased();
OS.memory().copyMemory(fromAddress, addressForWritePosition(), length);
uncheckedWritePosition(writePosition() + length);
}
@Override
public Bytes append8bit( CharSequence cs, int start, int end)
throws IllegalArgumentException, BufferOverflowException, BufferUnderflowException,
IndexOutOfBoundsException {
throwExceptionIfClosed();
// check the start.
long pos = writePosition();
writeCheckOffset(pos, 0);
if (!(cs instanceof String) || pos + (end - start) * 3 + 5 >= safeLimit()) {
return super.append8bit(cs, start, end);
}
return append8bit0((String) cs, start, end - start);
}
@Override
public MappedBytes write8bit( CharSequence s, int start, int length) {
throwExceptionIfClosed();
ObjectUtils.requireNonNull(s);
// check the start.
long pos = writePosition();
writeCheckOffset(pos, 0);
if (!(s instanceof String) || pos + length * 3 + 5 >= safeLimit()) {
super.write8bit(s, start, length);
return this;
}
writeStopBit(length);
return append8bit0((String) s, start, length);
}
private MappedBytes append8bit0( String s, int start, int length) throws BufferOverflowException {
if (Jvm.isJava9Plus()) {
byte[] bytes = extractBytes(s);
long address = addressForWritePosition();
Memory memory = bytesStore().memory;
int i = 0;
for (; i < length - 3; i += 4) {
int c0 = bytes[i + start] & 0xff;
int c1 = bytes[i + start + 1] & 0xff;
int c2 = bytes[i + start + 2] & 0xff;
int c3 = bytes[i + start + 3] & 0xff;
memory.writeInt(address, (c3 << 24) | (c2 << 16) | (c1 << 8) | c0);
address += 4;
}
for (; i < length; i++) {
byte c = bytes[i + start];
memory.writeByte(address++, c);
}
writeSkip(length);
} else {
char[] chars = extractChars(s);
long address = addressForWritePosition();
Memory memory = bytesStore().memory;
int i = 0;
for (; i < length - 3; i += 4) {
int c0 = chars[i + start] & 0xff;
int c1 = chars[i + start + 1] & 0xff;
int c2 = chars[i + start + 2] & 0xff;
int c3 = chars[i + start + 3] & 0xff;
memory.writeInt(address, (c3 << 24) | (c2 << 16) | (c1 << 8) | c0);
address += 4;
}
for (; i < length; i++) {
char c = chars[i + start];
memory.writeByte(address++, (byte) c);
}
writeSkip(length);
}
return this;
}
@Override
public Bytes appendUtf8( CharSequence cs, int start, int length)
throws BufferOverflowException, IllegalArgumentException {
throwExceptionIfClosed();
// check the start.
long pos = writePosition();
writeCheckOffset(pos, 0);
if (!(cs instanceof String) || pos + length * 3 + 5 >= safeLimit()) {
super.appendUtf8(cs, start, length);
return this;
}
if (Jvm.isJava9Plus()) {
// byte[] bytes = extractBytes((String) cs);
final String str = (String) cs;
long address = addressForWrite(pos);
Memory memory = OS.memory();
int i = 0;
non_ascii:
{
for (; i < length; i++) {
char c = str.charAt(i + start);
//byte c = bytes[i + start];
if (c > 127) {
writeSkip(i);
break non_ascii;
}
memory.writeByte(address++, (byte) c);
}
writeSkip(length);
return this;
}
for (; i < length; i++) {
char c = str.charAt(i + start);
appendUtf8(c);
}
} else {
char[] chars = extractChars((String) cs);
long address = addressForWrite(pos);
Memory memory = OS.memory();
int i = 0;
non_ascii:
{
for (; i < length; i++) {
char c = chars[i + start];
if (c > 127) {
writeSkip(i);
break non_ascii;
}
memory.writeByte(address++, (byte) c);
}
writeSkip(length);
return this;
}
for (; i < length; i++) {
char c = chars[i + start];
appendUtf8(c);
}
}
return this;
}
@Override
public boolean sharedMemory() {
return true;
}
@Override
public Bytes writeOrderedInt(long offset, int i) throws BufferOverflowException {
throwExceptionIfClosed();
writeCheckOffset(offset, 4);
if (bytesStore == null || !bytesStore.inside(offset, 4)) {
acquireNextByteStore0(offset, false);
}
bytesStore.writeOrderedInt(offset, i);
return this;
}
@Override
public byte readVolatileByte(long offset) throws BufferUnderflowException {
throwExceptionIfClosed();
if (bytesStore == null || !bytesStore.inside(offset, 1)) {
acquireNextByteStore0(offset, false);
}
return bytesStore.readVolatileByte(offset);
}
@Override
public short readVolatileShort(long offset) throws BufferUnderflowException {
throwExceptionIfClosed();
if (bytesStore == null || !bytesStore.inside(offset, 2)) {
acquireNextByteStore0(offset, false);
}
return bytesStore.readVolatileShort(offset);
}
@Override
public int readVolatileInt(long offset) throws BufferUnderflowException {
throwExceptionIfClosed();
if (bytesStore == null || !bytesStore.inside(offset, 4)) {
acquireNextByteStore0(offset, false);
}
return bytesStore.readVolatileInt(offset);
}
@Override
public long readVolatileLong(long offset) throws BufferUnderflowException {
throwExceptionIfClosed();
if (bytesStore == null || !bytesStore.inside(offset, 8)) {
acquireNextByteStore0(offset, false);
}
return bytesStore.readVolatileLong(offset);
}
@Override
public int peekUnsignedByte() {
throwExceptionIfClosed();
if (bytesStore == null || !bytesStore.inside(readPosition, 1)) {
acquireNextByteStore0(readPosition, false);
}
return super.peekUnsignedByte();
}
@Override
public int peekUnsignedByte(final long offset) throws BufferUnderflowException {
throwExceptionIfClosed();
if (bytesStore == null || !bytesStore.inside(offset, 1)) {
acquireNextByteStore0(offset, false);
}
return super.peekUnsignedByte(offset);
}
@SuppressWarnings("restriction")
@Override
public int peekVolatileInt() {
if (bytesStore == null || !bytesStore.inside(readPosition, 4)) {
acquireNextByteStore0(readPosition, true);
}
MappedBytesStore bytesStore = this.bytesStore;
long address = bytesStore.address + bytesStore.translate(readPosition);
Memory memory = bytesStore.memory;
// are we inside a cache line?
if ((address & 63) <= 60) {
ObjectUtils.requireNonNull(memory);
UnsafeMemory.unsafeLoadFence();
return UnsafeMemory.unsafeGetInt(address);
} else {
return memory.readVolatileInt(address);
}
}
@Override
public void release(ReferenceOwner id) throws IllegalStateException {
super.release(id);
closed |= refCount() <= 0;
}
@Override
public void releaseLast(ReferenceOwner id) throws IllegalStateException {
super.releaseLast(id);
closed = true;
}
@Override
public void close() {
if (closed)
return;
release(INIT);
closed = true;
}
@Override
public boolean isClosed() {
return closed;
}
@Override
public Bytes writeUtf8(CharSequence str) throws BufferOverflowException {
throwExceptionIfClosed();
if (str instanceof String) {
writeUtf8((String) str);
return this;
}
if (str == null) {
this.writeStopBit(-1);
} else {
try {
long utfLength = AppendableUtil.findUtf8Length(str);
this.writeStopBit(utfLength);
BytesInternal.appendUtf8(this, str, 0, str.length());
} catch (IndexOutOfBoundsException e) {
throw new AssertionError(e);
}
}
return this;
}
@Override
public Bytes writeUtf8(String str) throws BufferOverflowException {
throwExceptionIfClosed();
if (str == null) {
writeStopBit(-1);
return this;
}
try {
if (Jvm.isJava9Plus()) {
byte[] strBytes = extractBytes(str);
byte coder = getStringCoder(str);
long utfLength = AppendableUtil.findUtf8Length(strBytes, coder);
writeStopBit(utfLength);
appendUtf8(strBytes, 0, str.length(), coder);
} else {
char[] chars = extractChars(str);
long utfLength = AppendableUtil.findUtf8Length(chars);
writeStopBit(utfLength);
appendUtf8(chars, 0, chars.length);
}
return this;
} catch (IllegalArgumentException e) {
throw new AssertionError(e);
}
}
@Override
public Bytes appendUtf8(char[] chars, int offset, int length) throws BufferOverflowException, IllegalArgumentException {
throwExceptionIfClosed();
if (writePosition < 0 || writePosition > capacity() - (long) 1 + length)
throw writeBufferOverflowException(writePosition);
int i;
ascii:
{
for (i = 0; i < length; i++) {
char c = chars[offset + i];
if (c > 0x007F)
break ascii;
long oldPosition = writePosition;
if (bytesStore == null || ((writePosition & 0xff) == 0 && !bytesStore.inside(writePosition, (length - i) * 3))) {
acquireNextByteStore0(writePosition, false);
}
this.writePosition = writePosition + (long) 1;
bytesStore.writeByte(oldPosition, (byte) c);
}
return this;
}
for (; i < length; i++) {
char c = chars[offset + i];
BytesInternal.appendUtf8Char(this, c);
}
return this;
}
@Override
public long readStopBit() throws IORuntimeException {
throwExceptionIfClosed();
long offset = readOffsetPositionMoved(1);
byte l = bytesStore.readByte(offset);
if (l >= 0)
return l;
return BytesInternal.readStopBit0(this, l);
}
@Override
public char readStopBitChar() throws IORuntimeException {
throwExceptionIfClosed();
long offset = readOffsetPositionMoved(1);
byte l = bytesStore.readByte(offset);
if (l >= 0)
return (char) l;
return (char) BytesInternal.readStopBit0(this, l);
}
@Override
public Bytes writeStopBit(long n) throws BufferOverflowException {
throwExceptionIfClosed();
if ((n & ~0x7F) == 0) {
writeByte((byte) (n & 0x7f));
return this;
}
if ((~n & ~0x7F) == 0) {
writeByte((byte) (0x80L | ~n));
writeByte((byte) 0);
return this;
}
if ((n & ~0x3FFF) == 0) {
writeByte((byte) ((n & 0x7f) | 0x80));
writeByte((byte) (n >> 7));
return this;
}
BytesInternal.writeStopBit0(this, n);
return this;
}
@Override
public Bytes writeStopBit(char n) throws BufferOverflowException {
throwExceptionIfClosed();
if ((n & ~0x7F) == 0) {
writeByte((byte) (n & 0x7f));
return this;
}
if ((n & ~0x3FFF) == 0) {
writeByte((byte) ((n & 0x7f) | 0x80));
writeByte((byte) (n >> 7));
return this;
}
BytesInternal.writeStopBit0(this, n);
return this;
}
@Override
public boolean isDirectMemory() {
return true;
}
public MappedBytes write8bit( BytesStore bs)
throws BufferOverflowException {
throwExceptionIfClosed();
if (bs == null) {
writeStopBit(-1);
} else {
long offset = bs.readPosition();
long readRemaining = Math.min(writeRemaining(), bs.readLimit() - offset);
writeStopBit(readRemaining);
write(bs, offset, readRemaining);
}
return this;
}
// used by the Pretoucher, don't change this without considering the impact.
@Override
public boolean compareAndSwapLong(long offset, long expected, long value) throws BufferOverflowException {
throwExceptionIfClosed();
if (offset < 0 || offset > mappedFile.capacity() - (long) 8)
throw writeBufferOverflowException(offset);
if (bytesStore == null || bytesStore.start() > offset || offset + 8 >= bytesStore.safeLimit()) {
acquireNextByteStore0(offset, false);
}
// super.writeCheckOffset(offset, adding);
return bytesStore.compareAndSwapLong(offset, expected, value);
}
}