All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.openhft.chronicle.bytes.UncheckedNativeBytes Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2016-2022 chronicle.software
 *
 *     https://chronicle.software
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.openhft.chronicle.bytes;

import net.openhft.chronicle.bytes.internal.*;
import net.openhft.chronicle.bytes.internal.migration.HashCodeEqualsUtil;
import net.openhft.chronicle.bytes.render.DecimalAppender;
import net.openhft.chronicle.bytes.render.Decimaliser;
import net.openhft.chronicle.bytes.render.StandardDecimaliser;
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.annotation.NonNegative;
import net.openhft.chronicle.core.io.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;

import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static net.openhft.chronicle.core.Jvm.uncheckedCast;
import static net.openhft.chronicle.core.util.Ints.requireNonNegative;
import static net.openhft.chronicle.core.util.Longs.requireNonNegative;
import static net.openhft.chronicle.core.util.ObjectUtils.requireNonNull;

/**
 * An optimized extension of AbstractBytes that performs unchecked read and write operations
 * on a Bytes instance that is backed by native memory.
 * The class bypasses bounds checking to provide high-performance access to the underlying data.
 *
 * 

This class is intended for use in performance-critical scenarios where the client can * ensure that all operations stay within valid bounds, thus avoiding the overhead of bounds checking. * *

Warning: Using this class improperly can result in IndexOutOfBoundsException being thrown, * corruption of data, JVM crashes, or other undefined behavior. * * @param The type of the object this Bytes can point to. */ @SuppressWarnings("rawtypes") public class UncheckedNativeBytes extends AbstractReferenceCounted implements Bytes, HasUncheckedRandomDataInput, DecimalAppender { private static final byte[] MIN_VALUE_TEXT = ("" + Long.MIN_VALUE).getBytes(ISO_8859_1); @Deprecated(/* to remove in x.28 */) private static final boolean APPEND_0 = Jvm.getBoolean("bytes.append.0", true); // The real capacity of the BytesStore this UncheckedNativeBytes operates on protected final long capacity; // An instance of UncheckedRandomDataInput for accessing data without bounds checking private final UncheckedRandomDataInput uncheckedRandomDataInput = new UncheckedRandomDataInputHolder(); // The Bytes instance this UncheckedNativeBytes operates on @NotNull private final Bytes underlyingBytes; // The BytesStore that the underlying Bytes operates on @NotNull protected BytesStore bytesStore; // The position of the next byte to be read protected long readPosition; // The position of the next byte to be written protected long writePosition; // The limit of the write buffer protected long writeLimit; // Tracks the number of decimal places in the last number read private int lastDecimalPlaces = 0; // Tracks if the last number read had any digits private boolean lastNumberHadDigits = false; private Decimaliser decimaliser = StandardDecimaliser.STANDARD; private boolean append0 = APPEND_0; /** * Constructs an UncheckedNativeBytes instance by wrapping around the provided Bytes object. * * @param underlyingBytes the Bytes object to wrap around * @throws ClosedIllegalStateException If the resource has been released or closed. * @throws ThreadingIllegalStateException If this resource was accessed by multiple threads in an unsafe way */ @SuppressWarnings({"unchecked", "this-escape"}) public UncheckedNativeBytes(@NotNull Bytes underlyingBytes) throws IllegalStateException { this.underlyingBytes = underlyingBytes; underlyingBytes.reserve(this); this.bytesStore = BytesInternal.failIfBytesOnBytes(underlyingBytes.bytesStore()); assert bytesStore.start() == 0; writePosition = underlyingBytes.writePosition(); readPosition = underlyingBytes.readPosition(); capacity = bytesStore.realCapacity(); writeLimit = Math.min(capacity, underlyingBytes.writeLimit()); } @Override public void ensureCapacity(@NonNegative long desiredCapacity) throws IllegalArgumentException, IllegalStateException { if (desiredCapacity > realCapacity()) { underlyingBytes.ensureCapacity(desiredCapacity); bytesStore = uncheckedCast(underlyingBytes.bytesStore()); } } @Override public boolean unchecked() { return true; } @Override public boolean isDirectMemory() { return true; } @Override @NotNull public Bytes unchecked(boolean unchecked) { throwExceptionIfReleased(); return this; } @Override public void move(@NonNegative long from, @NonNegative long to, @NonNegative long length) throws IllegalStateException, BufferUnderflowException { ensureCapacity(to + length); bytesStore.move(from - start(), to - start(), length); } @NotNull @Override public Bytes compact() throws ClosedIllegalStateException, ThreadingIllegalStateException { // Get the start position of the buffer long start = start(); // Get the number of unread bytes in the buffer, ensuring that it is not set to a negative value long readRemaining = Math.max(0, readRemaining()); // if the space freed is less a than 1/4 the data that would be moved, leave it. if ((readPosition - start) < readRemaining / 4) return this; // Check if there are unread bytes and if they're not already at the start of the buffer if (readRemaining > 0 && start < readPosition) { // Move the unread bytes to the start of the buffer bytesStore.move(readPosition, start, readRemaining); } // Reset the read position to the start of the buffer readPosition = start; // Set the write position to be after the unread bytes writePosition = start + readRemaining; // Return this Bytes object to allow for method chaining return this; } @NotNull @Override public Bytes readPosition(@NonNegative long position) { assert position <= bytesStore.capacity(); readPosition = position; return this; } @NotNull @Override public Bytes readLimit(@NonNegative long limit) { assert limit <= bytesStore.capacity(); writePosition = limit; return this; } @NotNull @Override public Bytes writePosition(@NonNegative long position) { assert position <= bytesStore.capacity(); writePosition = requireNonNegative(position); return this; } @NotNull @Override public Bytes readSkip(long bytesToSkip) { readPosition += bytesToSkip; assert readPosition <= readLimit(); return this; } @Override public byte readVolatileByte(@NonNegative long offset) throws BufferUnderflowException { return bytesStore.readVolatileByte(offset); } @Override public short readVolatileShort(@NonNegative long offset) throws BufferUnderflowException { return bytesStore.readVolatileShort(offset); } @Override public int readVolatileInt(@NonNegative long offset) throws BufferUnderflowException { return bytesStore.readVolatileInt(offset); } @Override public long readVolatileLong(@NonNegative long offset) throws BufferUnderflowException { return bytesStore.readVolatileLong(offset); } @Override public void uncheckedReadSkipOne() { readPosition++; } @Override public void uncheckedReadSkipBackOne() { readPosition--; } @NotNull @Override public Bytes writeSkip(long bytesToSkip) { writePosition += bytesToSkip; return this; } @NotNull @Override public Bytes writeLimit(@NonNegative long limit) { assert limit <= bytesStore.capacity(); writeLimit = requireNonNegative(limit); return this; } @NotNull @Override public BytesStore, U> copy() { throwExceptionIfReleased(); throw new UnsupportedOperationException("todo"); } @Override public boolean isElastic() { return false; } protected long readOffsetPositionMoved(@NonNegative long adding) { long offset = readPosition; readPosition += adding; // TODO FIX MoldUdpHandlerTest // assert readPosition <= readLimit(); return offset; } protected long writeOffsetPositionMoved(@NonNegative long adding) { return writeOffsetPositionMoved(adding, adding); } protected long writeOffsetPositionMoved(@NonNegative long adding, @NonNegative long advance) { long oldPosition = writePosition; long writeEnd = oldPosition + adding; assert writeEnd <= bytesStore.safeLimit(); writePosition += advance; assert readPosition < writeLimit(); return oldPosition; } protected long prewriteOffsetPositionMoved(@NonNegative long substracting) { readPosition -= substracting; return readPosition; } @Override @NotNull public Bytes clear() { readPosition = writePosition = start(); writeLimit = capacity(); return this; } @NotNull @Override public Bytes clearAndPad(@NonNegative long length) throws BufferOverflowException { if (start() + length > capacity()) throw new BufferOverflowException(); readPosition = writePosition = start() + length; writeLimit = capacity(); return this; } @Override public long readLimit() { return writePosition; } @Override public long writeLimit() { return writeLimit; } @Override public @NonNegative long realCapacity() { return bytesStore.realCapacity(); } @Override public long realWriteRemaining() { return writeRemaining(); } @Override public @NonNegative long capacity() { return capacity; } @Nullable @Override public U underlyingObject() { return bytesStore.underlyingObject(); } @Override public @NonNegative long readPosition() { return readPosition; } @Override public @NonNegative long writePosition() { return writePosition; } @Override public boolean compareAndSwapInt(@NonNegative long offset, int expected, int value) throws BufferOverflowException, IllegalStateException { writeCheckOffset(offset, 4); return bytesStore.compareAndSwapInt(offset, expected, value); } @Override public void testAndSetInt(@NonNegative long offset, int expected, int value) throws BufferOverflowException, IllegalStateException { writeCheckOffset(offset, 4); bytesStore.testAndSetInt(offset, expected, value); } @Override public boolean compareAndSwapLong(@NonNegative long offset, long expected, long value) throws BufferOverflowException, IllegalStateException { writeCheckOffset(offset, 8); return bytesStore.compareAndSwapLong(offset, expected, value); } @Override protected void performRelease() throws IllegalStateException { this.underlyingBytes.release(this); final boolean interrupted = Thread.interrupted(); try { // need to wait as checks rely on this completing. BackgroundResourceReleaser.releasePendingResources(); } finally { if (interrupted) Thread.currentThread().interrupt(); } } @Override public int readUnsignedByte() { long offset = readOffsetPositionMoved(1); return bytesStore.readByte(offset) & 0xFF; } @Override public int uncheckedReadUnsignedByte() { return readUnsignedByte(); } @Override public byte readByte() { long offset = readOffsetPositionMoved(1); return bytesStore.readByte(offset); } @Override public int peekUnsignedByte() { try { return readRemaining() > 0 ? bytesStore.readUnsignedByte(readPosition) : -1; } catch (BufferUnderflowException e) { return -1; } } @Override public short readShort() { long offset = readOffsetPositionMoved(2); return bytesStore.readShort(offset); } @Override public int readInt() { long offset = readOffsetPositionMoved(4); return bytesStore.readInt(offset); } @Override public long readLong() { long offset = readOffsetPositionMoved(8); return bytesStore.readLong(offset); } @Override public float readFloat() { long offset = readOffsetPositionMoved(4); return bytesStore.readFloat(offset); } @Override public double readDouble() { long offset = readOffsetPositionMoved(8); return bytesStore.readDouble(offset); } @Override public int readVolatileInt() { long offset = readOffsetPositionMoved(4); return bytesStore.readVolatileInt(offset); } @Override public long readVolatileLong() { long offset = readOffsetPositionMoved(8); return bytesStore.readVolatileLong(offset); } @NotNull @Override public Bytes writeByte(@NonNegative long offset, byte i) throws BufferOverflowException, IllegalStateException { writeCheckOffset(offset, 1); bytesStore.writeByte(offset, i); return this; } @Override public Bytes rawWriteByte(byte i8) throws BufferOverflowException, IllegalStateException { bytesStore.writeByte(writePosition++, i8); return this; } @NotNull @Override public Bytes writeShort(@NonNegative long offset, short i) throws BufferOverflowException, IllegalStateException { writeCheckOffset(offset, 2); bytesStore.writeShort(offset, i); return this; } @NotNull @Override public Bytes writeInt(@NonNegative long offset, int i) throws BufferOverflowException, IllegalStateException { writeCheckOffset(offset, 4); bytesStore.writeInt(offset, i); return this; } @NotNull @Override public Bytes writeOrderedInt(@NonNegative long offset, int i) throws BufferOverflowException, IllegalStateException { writeCheckOffset(offset, 4); bytesStore.writeOrderedInt(offset, i); return this; } @NotNull @Override public Bytes writeLong(@NonNegative long offset, long i) throws BufferOverflowException, IllegalStateException { writeCheckOffset(offset, 8); bytesStore.writeLong(offset, i); return this; } @NotNull @Override public Bytes writeOrderedLong(@NonNegative long offset, long i) throws BufferOverflowException, IllegalStateException { writeCheckOffset(offset, 8); bytesStore.writeOrderedLong(offset, i); return this; } @NotNull @Override public Bytes writeFloat(@NonNegative long offset, float d) throws BufferOverflowException, IllegalStateException { writeCheckOffset(offset, 4); bytesStore.writeFloat(offset, d); return this; } @NotNull @Override public Bytes writeDouble(@NonNegative long offset, double d) throws BufferOverflowException, IllegalStateException { writeCheckOffset(offset, 8); bytesStore.writeDouble(offset, d); return this; } @NotNull @Override public Bytes writeVolatileByte(@NonNegative long offset, byte i8) throws BufferOverflowException, IllegalStateException { writeCheckOffset(offset, 1); bytesStore.writeVolatileByte(offset, i8); return this; } @NotNull @Override public Bytes writeVolatileShort(@NonNegative long offset, short i16) throws BufferOverflowException, IllegalStateException { writeCheckOffset(offset, 2); bytesStore.writeVolatileShort(offset, i16); return this; } @NotNull @Override public Bytes writeVolatileInt(@NonNegative long offset, int i32) throws BufferOverflowException, IllegalStateException { writeCheckOffset(offset, 4); bytesStore.writeVolatileInt(offset, i32); return this; } @NotNull @Override public Bytes writeVolatileLong(@NonNegative long offset, long i64) throws BufferOverflowException, IllegalStateException { writeCheckOffset(offset, 8); bytesStore.writeVolatileLong(offset, i64); return this; } @Override @NotNull public Bytes write(@NonNegative final long offsetInRDO, final byte[] byteArray, @NonNegative final int offset, @NonNegative int length) throws BufferOverflowException, IllegalStateException { requireNonNull(byteArray); writeCheckOffset(offsetInRDO, length); bytesStore.write(offsetInRDO, byteArray, offset, length); return this; } @Override public void write(@NonNegative long offsetInRDO, @NotNull ByteBuffer bytes, @NonNegative int offset, @NonNegative int length) throws BufferOverflowException, IllegalStateException { requireNonNull(bytes); writeCheckOffset(offsetInRDO, length); bytesStore.write(offsetInRDO, bytes, offset, length); } @Override @NotNull public Bytes write(@NonNegative long writeOffset, @NotNull RandomDataInput bytes, @NonNegative long readOffset, @NonNegative long length) throws BufferUnderflowException, BufferOverflowException, IllegalStateException { writeCheckOffset(writeOffset, length); bytesStore.write(writeOffset, bytes, readOffset, length); return this; } @SuppressWarnings("EmptyMethod") void writeCheckOffset(@NonNegative long offset, long adding) throws BufferOverflowException { // Do nothing } @Override public byte readByte(@NonNegative long offset) { return bytesStore.readByte(offset); } @Override public int readUnsignedByte(@NonNegative long offset) { return bytesStore.readByte(offset) & 0xFF; } @Override public int peekUnsignedByte(@NonNegative long offset) { return offset < start() || writePosition <= offset ? -1 : readByte(offset); } @Override public short readShort(@NonNegative long offset) { return bytesStore.readShort(offset); } @Override public int readInt(@NonNegative long offset) { return bytesStore.readInt(offset); } @Override public long readLong(@NonNegative long offset) { return bytesStore.readLong(offset); } @Override public float readFloat(@NonNegative long offset) { return bytesStore.readFloat(offset); } @Override public double readDouble(@NonNegative long offset) { return bytesStore.readDouble(offset); } @NotNull @Override public Bytes writeByte(byte i8) { long offset = writeOffsetPositionMoved(1); bytesStore.writeByte(offset, i8); return this; } @NotNull @Override public Bytes prewriteByte(byte i8) { long offset = prewriteOffsetPositionMoved(1); bytesStore.writeByte(offset, i8); return this; } @NotNull @Override public Bytes writeShort(short i16) throws IllegalStateException { long offset = writeOffsetPositionMoved(2); bytesStore.writeShort(offset, i16); return this; } @NotNull @Override public Bytes prewriteShort(short i16) throws IllegalStateException { long offset = prewriteOffsetPositionMoved(2); bytesStore.writeShort(offset, i16); return this; } @NotNull @Override public Bytes writeInt(int i) throws IllegalStateException { long offset = writeOffsetPositionMoved(4); bytesStore.writeInt(offset, i); return this; } @NotNull @Override public Bytes writeIntAdv(int i, @NonNegative int advance) throws IllegalStateException { long offset = writeOffsetPositionMoved(4, advance); bytesStore.writeInt(offset, i); return this; } @NotNull @Override public Bytes prewriteInt(int i) throws IllegalStateException { long offset = prewriteOffsetPositionMoved(4); bytesStore.writeInt(offset, i); return this; } @NotNull @Override public Bytes writeLong(long i64) throws IllegalStateException { long offset = writeOffsetPositionMoved(8); bytesStore.writeLong(offset, i64); return this; } @NotNull @Override public Bytes writeLongAdv(long i64, @NonNegative int advance) throws IllegalStateException { long offset = writeOffsetPositionMoved(8, advance); bytesStore.writeLong(offset, i64); return this; } @NotNull @Override public Bytes prewriteLong(long i64) throws IllegalStateException { long offset = prewriteOffsetPositionMoved(8); bytesStore.writeLong(offset, i64); return this; } @NotNull @Override public Bytes writeFloat(float f) throws IllegalStateException { long offset = writeOffsetPositionMoved(4); bytesStore.writeFloat(offset, f); return this; } @NotNull @Override public Bytes writeDouble(double d) throws IllegalStateException { long offset = writeOffsetPositionMoved(8); bytesStore.writeDouble(offset, d); return this; } @NotNull @Override public Bytes writeDoubleAndInt(double d, int i) throws IllegalStateException { long offset = writeOffsetPositionMoved(12); bytesStore.writeDouble(offset, d); bytesStore.writeInt(offset + 8, i); return this; } @NotNull @Override public Bytes write(final byte[] byteArray, @NonNegative final int offset, @NonNegative final int length) throws BufferOverflowException, IllegalStateException, ArrayIndexOutOfBoundsException { requireNonNegative(offset); requireNonNegative(length); if (length + offset > byteArray.length) throw new ArrayIndexOutOfBoundsException("bytes.length=" + byteArray.length + ", " + "length=" + length + ", offset=" + offset); if (length > writeRemaining()) throw new BufferOverflowException(); long offsetInRDO = writeOffsetPositionMoved(length); bytesStore.write(offsetInRDO, byteArray, offset, length); return this; } @NotNull @Override public Bytes prewrite(@NotNull byte[] bytes) throws IllegalStateException, BufferOverflowException { long offsetInRDO = prewriteOffsetPositionMoved(bytes.length); bytesStore.write(offsetInRDO, bytes); return this; } @NotNull @Override public Bytes prewrite(@NotNull BytesStore bytes) throws IllegalStateException, BufferOverflowException { long offsetInRDO = prewriteOffsetPositionMoved(bytes.length()); bytesStore.write(offsetInRDO, bytes); return this; } @NotNull @Override public Bytes writeSome(@NotNull ByteBuffer buffer) throws IllegalStateException { bytesStore.write(writePosition, buffer, buffer.position(), buffer.limit()); writePosition += buffer.remaining(); assert writePosition <= writeLimit(); return this; } @NotNull @Override public Bytes writeOrderedInt(int i) throws IllegalStateException { long offset = writeOffsetPositionMoved(4); bytesStore.writeOrderedInt(offset, i); return this; } @NotNull @Override public Bytes writeOrderedLong(long i) throws IllegalStateException { long offset = writeOffsetPositionMoved(8); bytesStore.writeOrderedLong(offset, i); return this; } @Override public long addressForRead(@NonNegative long offset) throws BufferUnderflowException { return bytesStore.addressForRead(offset); } @Override public long addressForWrite(@NonNegative long offset) throws BufferOverflowException { return bytesStore.addressForWrite(offset); } @Override public long addressForWritePosition() throws UnsupportedOperationException, BufferOverflowException { return bytesStore.addressForWrite(0); } @Override public int hashCode() { return HashCodeEqualsUtil.hashCode(this); } @Override public boolean equals(Object obj) { return obj instanceof BytesStore && BytesInternal.contentEqual(this, (BytesStore) obj); } @NotNull @Override public String toString() { if (refCount() <= 0) return "(released)"; return BytesInternal.toString(this); } @Override public void lenient(boolean lenient) { throw new UnsupportedOperationException(); } @Override public boolean lenient() { return false; } @Override public void nativeRead(@NonNegative long position, long address, @NonNegative long size) throws IllegalStateException, BufferUnderflowException { bytesStore.nativeRead(position, address, size); } @Override public void nativeWrite(long address, @NonNegative long position, @NonNegative long size) throws IllegalStateException, BufferOverflowException { bytesStore.nativeWrite(address, position, size); } @Nullable @Override public BytesStore bytesStore() { return bytesStore; } @Override public int byteCheckSum() throws IORuntimeException { @Nullable NativeBytesStore nativeBytesStore = (NativeBytesStore) bytesStore(); return nativeBytesStore.byteCheckSum(readPosition(), readLimit()); } @Override @NotNull public Bytes append8bit(@NotNull CharSequence cs) throws BufferOverflowException, BufferUnderflowException, IllegalStateException { if (cs instanceof BytesStore) { return write((BytesStore) cs); } int length = cs.length(); long offset = writeOffsetPositionMoved(length); long address = bytesStore.addressForWrite(offset); @Nullable Memory memory = UnsafeMemory.MEMORY; assert memory != null; int i = 0; for (; i < length - 1; i += 2) { char c = cs.charAt(i); char c2 = cs.charAt(i + 1); memory.writeByte(address + i, (byte) c); memory.writeByte(address + i + 1, (byte) c2); } for (; i < length; i++) { char c = cs.charAt(i); memory.writeByte(address + i, (byte) c); } return this; } @NotNull @Override public Bytes appendUtf8(char[] chars, @NonNegative int offset, @NonNegative int length) throws BufferOverflowException, IllegalArgumentException, IllegalStateException { long actualUTF8Length = AppendableUtil.findUtf8Length(chars, offset, length); ensureCapacity(writePosition + actualUTF8Length); @NotNull BytesStore nbs = this.bytesStore; long position = ((NativeBytesStore) nbs).appendUtf8(writePosition(), chars, offset, length); writePosition(position); return this; } @Override public @NotNull UncheckedNativeBytes append(double d) throws BufferOverflowException, IllegalStateException { if (!decimaliser.toDecimal(d, this)) append8bit(Double.toString(d)); return this; } @Override public @NotNull UncheckedNativeBytes append(float f) throws BufferOverflowException, IllegalStateException { if (!decimaliser.toDecimal(f, this)) append8bit(Float.toString(f)); return this; } @Override public @NotNull Bytes append(int value) throws BufferOverflowException, IllegalArgumentException, IllegalStateException { append(value < 0, Math.abs((long) value), 0); return this; } @Override public @NotNull Bytes append(long value) throws BufferOverflowException, IllegalStateException { if (value == Long.MIN_VALUE) write(MIN_VALUE_TEXT); else append(value < 0, Math.abs(value), 0); return this; } @Override public Decimaliser decimaliser() { return decimaliser; } @Override public Bytes decimaliser(Decimaliser decimaliser) { this.decimaliser = decimaliser; return this; } @SuppressWarnings("deprecation") @Override public boolean fpAppend0() { return append0; } @SuppressWarnings("deprecation") @Override public Bytes fpAppend0(boolean append0) { this.append0 = append0; return this; } @Override public void append(boolean negative, long mantissa, int exponent) { ensureCapacity(writePosition() + BytesInternal.digitsForExponent(exponent)); long length = bytesStore().appendAndReturnLength(writePosition(), negative, mantissa, exponent, fpAppend0()); writeSkip(length); } @Override public long appendAndReturnLength(long writePosition, boolean negative, long mantissa, int exponent, boolean append0) { return bytesStore().appendAndReturnLength(writePosition, negative, mantissa, exponent, append0); } @Override public int lastDecimalPlaces() { return lastDecimalPlaces; } @Override public void lastDecimalPlaces(int lastDecimalPlaces) { this.lastDecimalPlaces = Math.max(0, lastDecimalPlaces); } @Override public boolean lastNumberHadDigits() { return lastNumberHadDigits; } @Override public void lastNumberHadDigits(boolean lastNumberHadDigits) { this.lastNumberHadDigits = lastNumberHadDigits; } @Override public long write8bit(@NonNegative long position, @NotNull BytesStore bs) { return bytesStore.write8bit(position, bs); } @Override public long write8bit(@NonNegative long position, @NotNull String s, @NonNegative int start, @NonNegative int length) { return bytesStore.write8bit(position, s, start, length); } public Bytes write8bit(@Nullable BytesStore bs) throws BufferOverflowException, IllegalStateException, BufferUnderflowException { if (bs == null) { BytesInternal.writeStopBitNeg1(this); } else { final long offset = bs.readPosition(); final long readRemaining = Math.min(writeRemaining(), bs.readLimit() - offset); writeStopBit(readRemaining); write(bs, offset, readRemaining); } return this; } @Override public @NotNull Bytes write8bit(final @NotNull String text, final @NonNegative int start, final @NonNegative int length) { requireNonNull(text); final long toWriteLength = UnsafeMemory.INSTANCE.stopBitLength(length) + (long) length; final long position = writeOffsetPositionMoved(toWriteLength, 0); bytesStore.write8bit(position, text, start, length); writePosition += toWriteLength; return this; } @Override public @NotNull UncheckedRandomDataInput acquireUncheckedInput() { return uncheckedRandomDataInput; } @Override public void unmonitor() { super.unmonitor(); Monitorable.unmonitor(bytesStore); } private final class UncheckedRandomDataInputHolder implements UncheckedRandomDataInput { @Override public byte readByte(@NonNegative long offset) { return bytesStore.readByte(offset); } @Override public short readShort(@NonNegative long offset) { return bytesStore.readShort(offset); } @Override public int readInt(@NonNegative long offset) { return bytesStore.readInt(offset); } @Override public long readLong(@NonNegative long offset) { return bytesStore.readLong(offset); } } }