@SuppressWarnings("sunapi") public class NativeBytesStore<Underlying> implements BytesStore<NativeBytesStore<Underlying>, Underlying> { private static final long MEMORY_MAPPED_SIZE = 128 << 10; private static final Logger LOGGER = LoggerFactory.getLogger(NativeBytesStore.class); @Nullable private final Cleaner cleaner; private final boolean elastic; @Nullable private final Underlying underlyingObject; @Nullable private final Throwable createdHere = Jvm.isDebug() ? new Throwable("Created here") : null; // on release, set this to null. @Nullable protected Memory memory = OS.memory(); private final ReferenceCounter refCount = ReferenceCounter.onReleased(this::performRelease); protected long address; long maximumLimit; private Error releasedHere; private NativeBytesStore(@NotNull ByteBuffer bb, boolean elastic) { this.elastic = elastic; underlyingObject = (Underlying) bb; setAddress(((DirectBuffer) bb).address()); this.maximumLimit = bb.capacity(); cleaner = ((DirectBuffer) bb).cleaner(); } public NativeBytesStore(long address, long maximumLimit) { this(address, maximumLimit, null, false); } public NativeBytesStore( long address, long maximumLimit, @Nullable Runnable deallocator, boolean elastic) { setAddress(address); this.maximumLimit = maximumLimit; cleaner = deallocator == null ? null : Cleaner.create(this, deallocator); underlyingObject = null; this.elastic = elastic; } @NotNull public static NativeBytesStore<ByteBuffer> wrap(@NotNull ByteBuffer bb) { return new NativeBytesStore<>(bb, false); } /** * this is an elastic native store * * @param capacity of the buffer. */ @NotNull public static NativeBytesStore<Void> nativeStore(long capacity) throws IllegalArgumentException { return of(capacity, true, true); } @NotNull private static NativeBytesStore<Void> of(long capacity, boolean zeroOut, boolean elastic) throws IllegalArgumentException { Memory memory = OS.memory(); long address = memory.allocate(capacity); if (zeroOut || capacity < MEMORY_MAPPED_SIZE) { memory.setMemory(address, capacity, (byte) 0); memory.storeFence(); } Deallocator deallocator = new Deallocator(address, capacity); return new NativeBytesStore<>(address, capacity, deallocator, elastic); } @NotNull public static NativeBytesStore<Void> nativeStoreWithFixedCapacity(long capacity) throws IllegalArgumentException { return of(capacity, true, false); } @NotNull public static NativeBytesStore<Void> lazyNativeBytesStoreWithFixedCapacity(long capacity) throws IllegalArgumentException { return of(capacity, false, false); } @NotNull public static NativeBytesStore<ByteBuffer> elasticByteBuffer() { return elasticByteBuffer(OS.pageSize()); } @NotNull public static NativeBytesStore<ByteBuffer> elasticByteBuffer(int size) { return new NativeBytesStore<>(ByteBuffer.allocateDirect(size), true); } @NotNull @Override public BytesStore<NativeBytesStore<Underlying>, Underlying> copy() throws IllegalStateException { if (underlyingObject == null) { NativeBytesStore<Void> copy = of(realCapacity(), false, true); OS.memory().copyMemory(address, copy.address, capacity()); return (BytesStore) copy; } else if (underlyingObject instanceof ByteBuffer) { ByteBuffer bb = ByteBuffer.allocateDirect(Maths.toInt32(capacity())); bb.put((ByteBuffer) underlyingObject); bb.clear(); return (BytesStore) wrap(bb); } else { throw new UnsupportedOperationException(); } } @Override public VanillaBytes<Underlying> bytesForWrite() throws IllegalStateException { return elastic ? new NativeBytes<>(this) : new VanillaBytes<>(this); } @Override @ForceInline public long realCapacity() { return maximumLimit; } @Override @ForceInline public long capacity() { return maximumLimit; } @Nullable @Override @ForceInline public Underlying underlyingObject() { return underlyingObject; } @NotNull @Override @ForceInline public NativeBytesStore<Underlying> zeroOut(long start, long end) throws IllegalArgumentException { if (start < writePosition() || end > writeLimit()) throw new IllegalArgumentException( "position: " + writePosition() + ", start: " + start + ", end: " + end + ", limit: " + writeLimit()); if (start >= end) return this; memory.setMemory(address + translate(start), end - start, (byte) 0); return this; } @Override @ForceInline public boolean compareAndSwapInt(long offset, int expected, int value) { return memory.compareAndSwapInt(address + translate(offset), expected, value); } @Override @ForceInline public boolean compareAndSwapLong(long offset, long expected, long value) { return memory.compareAndSwapLong(address + translate(offset), expected, value); } long translate(long offset) { long offset2 = offset - start(); // assert checkTranslatedBounds(offset2); return offset2; } public long start() { return 0L; } @Override public void reserve() throws IllegalStateException { refCount.reserve(); } @Override public void release() throws IllegalStateException { refCount.release(); if (Jvm.isDebug() && refCount.get() == 0) releasedHere = new Error("Released here"); } @Override public long refCount() { return refCount.get(); } @Override @ForceInline public byte readByte(long offset) { if (Jvm.isDebug()) checkReleased(); return memory.readByte(address + translate(offset)); } public void checkReleased() { if (releasedHere != null) throw new InternalError("Accessing a released resource", releasedHere); } @Override @ForceInline public short readShort(long offset) { return memory.readShort(address + translate(offset)); } @Override @ForceInline public int readInt(long offset) { return memory.readInt(address + translate(offset)); } @Override @ForceInline public long readLong(long offset) { return memory.readLong(address + translate(offset)); } @Override @ForceInline public float readFloat(long offset) { return memory.readFloat(address + translate(offset)); } @Override @ForceInline public double readDouble(long offset) { return memory.readDouble(address + translate(offset)); } @Override @ForceInline public byte readVolatileByte(long offset) { return memory.readVolatileByte(address + translate(offset)); } @Override @ForceInline public short readVolatileShort(long offset) { return memory.readVolatileShort(address + translate(offset)); } @Override @ForceInline public int readVolatileInt(long offset) { return memory.readVolatileInt(address + translate(offset)); } @Override @ForceInline public long readVolatileLong(long offset) { return memory.readVolatileLong(address + translate(offset)); } @NotNull @Override @ForceInline public NativeBytesStore<Underlying> writeByte(long offset, byte i8) { memory.writeByte(address + translate(offset), i8); return this; } @NotNull @Override @ForceInline public NativeBytesStore<Underlying> writeShort(long offset, short i16) { memory.writeShort(address + translate(offset), i16); return this; } @NotNull @Override @ForceInline public NativeBytesStore<Underlying> writeInt(long offset, int i32) { memory.writeInt(address + translate(offset), i32); return this; } @NotNull @Override @ForceInline public NativeBytesStore<Underlying> writeOrderedInt(long offset, int i) { memory.writeOrderedInt(address + translate(offset), i); return this; } @NotNull @Override @ForceInline public NativeBytesStore<Underlying> writeLong(long offset, long i64) { memory.writeLong(address + translate(offset), i64); return this; } @NotNull @Override @ForceInline public NativeBytesStore<Underlying> writeOrderedLong(long offset, long i) { memory.writeOrderedLong(address + translate(offset), i); return this; } @NotNull @Override @ForceInline public NativeBytesStore<Underlying> writeFloat(long offset, float f) { memory.writeFloat(address + translate(offset), f); return this; } @NotNull @Override @ForceInline public NativeBytesStore<Underlying> writeDouble(long offset, double d) { memory.writeDouble(address + translate(offset), d); return this; } @NotNull @Override @ForceInline public NativeBytesStore<Underlying> writeVolatileByte(long offset, byte i8) { memory.writeVolatileByte(address + translate(offset), i8); return this; } @NotNull @Override @ForceInline public NativeBytesStore<Underlying> writeVolatileShort(long offset, short i16) { memory.writeVolatileShort(address + translate(offset), i16); return this; } @NotNull @Override @ForceInline public NativeBytesStore<Underlying> writeVolatileInt(long offset, int i32) { memory.writeVolatileInt(address + translate(offset), i32); return this; } @NotNull @Override @ForceInline public NativeBytesStore<Underlying> writeVolatileLong(long offset, long i64) { memory.writeVolatileLong(address + translate(offset), i64); return this; } @NotNull @Override @ForceInline public NativeBytesStore<Underlying> write( long offsetInRDO, byte[] bytes, int offset, int length) { memory.copyMemory(bytes, offset, address + translate(offsetInRDO), length); return this; } @Override @ForceInline public void write(long offsetInRDO, @NotNull ByteBuffer bytes, int offset, int length) { if (bytes.isDirect()) { memory.copyMemory(((DirectBuffer) bytes).address(), address + translate(offsetInRDO), length); } else { memory.copyMemory(bytes.array(), offset, address + translate(offsetInRDO), length); } } @NotNull @Override @ForceInline public NativeBytesStore<Underlying> write( long offsetInRDO, @NotNull RandomDataInput bytes, long offset, long length) throws BufferOverflowException, BufferUnderflowException, IORuntimeException { // TODO optimize, call unsafe.copyMemory when possible, copy 4, 2 bytes at once long i = 0; for (; i < length - 7; i += 8) { writeLong(offsetInRDO + i, bytes.readLong(offset + i)); } for (; i < length; i++) { writeByte(offsetInRDO + i, bytes.readByte(offset + i)); } return this; } @Override public long address(long offset) throws UnsupportedOperationException { if (offset < start() || offset >= capacity()) throw new IllegalArgumentException(); return address + translate(offset); } private void performRelease() { if (refCount.get() > 0) { LOGGER.info("NativeBytesStore discarded without releasing ", createdHere); } memory = null; if (cleaner != null) cleaner.clean(); } @NotNull @Override public String toString() { try { return BytesInternal.toString(this); } catch (IllegalStateException | IORuntimeException e) { return e.toString(); } } @Override @ForceInline public void nativeRead(long position, long address, long size) { // TODO add bounds checking. OS.memory().copyMemory(address(position), address, size); } @Override @ForceInline public void nativeWrite(long address, long position, long size) { // TODO add bounds checking. this.memory.copyMemory(address, address(position), size); } void write8bit(long position, char[] chars, int offset, int length) { long addr = address + translate(position); Memory memory = this.memory; for (int i = 0; i < length; i++) memory.writeByte(addr + i, (byte) chars[offset + i]); } void read8bit(long position, char[] chars, int length) { long addr = address + translate(position); Memory memory = OS.memory(); for (int i = 0; i < length; i++) chars[i] = (char) (memory.readByte(addr + i) & 0xFF); } public long readIncompleteLong(long offset) { int remaining = (int) Math.min(8, readRemaining() - offset); long l = 0; for (int i = 0; i < remaining; i++) { byte b = memory.readByte(address + offset + i); l |= (long) (b & 0xFF) << (i * 8); } return l; } @Override public boolean equals(Object obj) { try { return obj instanceof BytesStore && BytesInternal.contentEqual(this, (BytesStore) obj); } catch (IORuntimeException e) { throw new AssertionError(e); } } public void setAddress(long address) { if ((address & ~0x3FFF) == 0) throw new AssertionError("Invalid address " + Long.toHexString(address)); this.address = address; } public long appendUTF(long pos, char[] chars, int offset, int length) { long address = this.address + translate(0); Memory memory = this.memory; int i; ascii: { for (i = 0; i < length; i++) { char c = chars[offset + i]; if (c > 0x007F) break ascii; memory.writeByte(address + pos++, (byte) c); } return pos; } return appendUTF0(pos, chars, offset, length, i); } private long appendUTF0(long pos, char[] chars, int offset, int length, int i) { for (; i < length; i++) { char c = chars[offset + i]; if (c <= 0x007F) { writeByte(pos++, (byte) c); } else if (c <= 0x07FF) { writeByte(pos++, (byte) (0xC0 | ((c >> 6) & 0x1F))); writeByte(pos++, (byte) (0x80 | c & 0x3F)); } else { writeByte(pos++, (byte) (0xE0 | ((c >> 12) & 0x0F))); writeByte(pos++, (byte) (0x80 | ((c >> 6) & 0x3F))); writeByte(pos++, (byte) (0x80 | (c & 0x3F))); } } return pos; } static class Deallocator implements Runnable { private volatile long address, size; Deallocator(long address, long size) { assert address != 0; this.address = address; this.size = size; } @Override public void run() { if (address == 0) return; address = 0; OS.memory().freeMemory(address, size); } } }
/** Fast unchecked version of AbstractBytes */ public class UncheckedNativeBytes<Underlying> implements Bytes<Underlying> { protected final long capacity; @Nullable protected NativeBytesStore<Underlying> bytesStore; private final ReferenceCounter refCount = ReferenceCounter.onReleased(this::performRelease); protected long readPosition; protected long writePosition; protected long writeLimit; private int lastDecimalPlaces = 0; public UncheckedNativeBytes(@NotNull Bytes<Underlying> underlyingBytes) throws IllegalStateException { underlyingBytes.reserve(); this.bytesStore = (NativeBytesStore<Underlying>) underlyingBytes.bytesStore(); assert bytesStore.start() == 0; writePosition = underlyingBytes.writePosition(); writeLimit = underlyingBytes.writeLimit(); readPosition = underlyingBytes.readPosition(); capacity = bytesStore.capacity(); } @NotNull public Bytes<Underlying> unchecked(boolean unchecked) { return this; } @NotNull @Override public Bytes<Underlying> readPosition(long position) { readPosition = position; return this; } @NotNull @Override public Bytes<Underlying> readLimit(long limit) { writePosition = limit; return this; } @NotNull @Override public Bytes<Underlying> writePosition(long position) { writePosition = position; return this; } @NotNull @Override public Bytes<Underlying> readSkip(long bytesToSkip) { readPosition += bytesToSkip; return this; } @NotNull @Override public Bytes<Underlying> writeSkip(long bytesToSkip) { writePosition += bytesToSkip; return this; } @NotNull @Override public Bytes<Underlying> writeLimit(long limit) { writeLimit = limit; return this; } @NotNull @Override public BytesStore<Bytes<Underlying>, Underlying> copy() { throw new UnsupportedOperationException("todo"); } @Override public boolean isElastic() { return false; } protected long readOffsetPositionMoved(long adding) { long offset = readPosition; readPosition += adding; return offset; } protected long writeOffsetPositionMoved(long adding) { long oldPosition = writePosition; writePosition += adding; return oldPosition; } protected long prewriteOffsetPositionMoved(long substracting) { return readPosition -= substracting; } @NotNull @Override public Bytes<Underlying> write(@NotNull BytesStore bytes, long offset, long length) throws IORuntimeException, BufferUnderflowException, BufferOverflowException { if (length == 8) { writeLong(bytes.readLong(offset)); } else if (bytes.bytesStore() instanceof NativeBytesStore && length >= 16) { rawCopy(bytes, offset, length); } else { BytesInternal.write(bytes, offset, length, this); } return this; } public void rawCopy(@NotNull BytesStore bytes, long offset, long length) throws BufferOverflowException, BufferUnderflowException { long len = Math.min(writeRemaining(), Math.min(bytes.readRemaining(), length)); if (len > 0) { writeCheckOffset(writePosition(), len); OS.memory().copyMemory(bytes.address(offset), address(writePosition()), len); writeSkip(len); } } @NotNull public Bytes<Underlying> clear() { readPosition = writePosition = start(); writeLimit = capacity(); return this; } @Override public Bytes<Underlying> clearAndPad(long length) throws BufferOverflowException { if (start() + length > capacity()) throw new BufferOverflowException(); readPosition = writePosition = start() + length; writeLimit = capacity(); return this; } @Override @ForceInline public long readLimit() { return writePosition; } @ForceInline public long writeLimit() { return writeLimit; } @Override @ForceInline public long realCapacity() { return capacity(); } @Override @ForceInline public long capacity() { return capacity; } @Nullable @Override public Underlying underlyingObject() { return bytesStore.underlyingObject(); } @Override @ForceInline public long readPosition() { return readPosition; } @Override @ForceInline public long writePosition() { return writePosition; } @Override @ForceInline public boolean compareAndSwapInt(long offset, int expected, int value) throws BufferOverflowException { writeCheckOffset(offset, 4); return bytesStore.compareAndSwapInt(offset, expected, value); } @Override @ForceInline public boolean compareAndSwapLong(long offset, long expected, long value) throws BufferOverflowException { writeCheckOffset(offset, 8); return bytesStore.compareAndSwapLong(offset, expected, value); } void performRelease() { this.bytesStore.release(); this.bytesStore = null; } @Override public int readUnsignedByte() { long offset = readOffsetPositionMoved(1); return bytesStore.memory.readByte(bytesStore.address + offset); } @Override @ForceInline public byte readByte() { long offset = readOffsetPositionMoved(1); return bytesStore.memory.readByte(bytesStore.address + offset); } @Override @ForceInline public int peekUnsignedByte() { try { return readRemaining() > 0 ? bytesStore.readUnsignedByte(readPosition) : -1; } catch (BufferUnderflowException | IORuntimeException e) { return -1; } } @Override @ForceInline public short readShort() { long offset = readOffsetPositionMoved(2); return bytesStore.readShort(offset); } @Override @ForceInline public int readInt() { long offset = readOffsetPositionMoved(4); return bytesStore.readInt(offset); } @Override @ForceInline public long readLong() { long offset = readOffsetPositionMoved(8); return bytesStore.readLong(offset); } @Override @ForceInline public float readFloat() { long offset = readOffsetPositionMoved(4); return bytesStore.readFloat(offset); } @Override @ForceInline public double readDouble() { long offset = readOffsetPositionMoved(8); return bytesStore.readDouble(offset); } @Override @ForceInline public int readVolatileInt() { long offset = readOffsetPositionMoved(4); return bytesStore.readVolatileInt(offset); } @Override @ForceInline public long readVolatileLong() { long offset = readOffsetPositionMoved(8); return bytesStore.readVolatileLong(offset); } @Override public void reserve() throws IllegalStateException { refCount.reserve(); } @Override public void release() throws IllegalStateException { refCount.release(); } @Override public long refCount() { return refCount.get(); } @NotNull @Override @ForceInline public Bytes<Underlying> writeByte(long offset, byte i) throws BufferOverflowException { writeCheckOffset(offset, 1); bytesStore.writeByte(offset, i); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeShort(long offset, short i) throws BufferOverflowException { writeCheckOffset(offset, 2); bytesStore.writeShort(offset, i); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeInt(long offset, int i) throws BufferOverflowException { writeCheckOffset(offset, 4); bytesStore.writeInt(offset, i); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeOrderedInt(long offset, int i) throws BufferOverflowException { writeCheckOffset(offset, 4); bytesStore.writeOrderedInt(offset, i); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeLong(long offset, long i) throws BufferOverflowException { writeCheckOffset(offset, 8); bytesStore.writeLong(offset, i); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeOrderedLong(long offset, long i) throws BufferOverflowException { writeCheckOffset(offset, 8); bytesStore.writeOrderedLong(offset, i); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeFloat(long offset, float d) throws BufferOverflowException { writeCheckOffset(offset, 4); bytesStore.writeFloat(offset, d); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeDouble(long offset, double d) throws BufferOverflowException { writeCheckOffset(offset, 8); bytesStore.writeDouble(offset, d); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeVolatileByte(long offset, byte i8) throws BufferOverflowException { writeCheckOffset(offset, 1); bytesStore.writeVolatileByte(offset, i8); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeVolatileShort(long offset, short i16) throws BufferOverflowException { writeCheckOffset(offset, 2); bytesStore.writeVolatileShort(offset, i16); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeVolatileInt(long offset, int i32) throws BufferOverflowException { writeCheckOffset(offset, 4); bytesStore.writeVolatileInt(offset, i32); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeVolatileLong(long offset, long i64) throws BufferOverflowException { writeCheckOffset(offset, 8); bytesStore.writeVolatileLong(offset, i64); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> write(long offsetInRDO, byte[] bytes, int offset, int length) throws BufferOverflowException { writeCheckOffset(offsetInRDO, length); bytesStore.write(offsetInRDO, bytes, offset, length); return this; } @Override @ForceInline public void write(long offsetInRDO, ByteBuffer bytes, int offset, int length) throws BufferOverflowException { writeCheckOffset(offsetInRDO, length); bytesStore.write(offsetInRDO, bytes, offset, length); } @NotNull @Override @ForceInline public Bytes<Underlying> write(long offsetInRDO, RandomDataInput bytes, long offset, long length) throws IORuntimeException, BufferUnderflowException, BufferOverflowException { writeCheckOffset(offsetInRDO, length); bytesStore.write(offsetInRDO, bytes, offset, length); return this; } @ForceInline void writeCheckOffset(long offset, long adding) throws BufferOverflowException { assert writeCheckOffset0(offset, adding); } private boolean writeCheckOffset0(long offset, long adding) throws BufferOverflowException { if (offset < start()) throw new BufferOverflowException(); if (offset + adding > writeLimit()) { assert offset + adding <= writeLimit() : "cant add bytes past the limit : limit=" + writeLimit() + ",offset=" + offset + ",adding=" + adding; throw new BufferOverflowException(); } return true; } @Override @ForceInline public byte readByte(long offset) { return bytesStore.readByte(offset); } @Override public int readUnsignedByte(long offset) { return bytesStore.memory.readByte(bytesStore.address + offset) & 0xFF; } @Override @ForceInline public short readShort(long offset) { return bytesStore.readShort(offset); } @Override @ForceInline public int readInt(long offset) { return bytesStore.readInt(offset); } @Override @ForceInline public long readLong(long offset) { return bytesStore.readLong(offset); } @Override @ForceInline public float readFloat(long offset) { return bytesStore.readFloat(offset); } @Override @ForceInline public double readDouble(long offset) { return bytesStore.readDouble(offset); } @NotNull @Override @ForceInline public Bytes<Underlying> writeByte(byte i8) { long offset = writeOffsetPositionMoved(1); bytesStore.memory.writeByte(bytesStore.address + offset, i8); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> prewriteByte(byte i8) { long offset = prewriteOffsetPositionMoved(1); bytesStore.memory.writeByte(bytesStore.address + offset, i8); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeShort(short i16) { long offset = writeOffsetPositionMoved(2); bytesStore.writeShort(offset, i16); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> prewriteShort(short i16) { long offset = prewriteOffsetPositionMoved(2); bytesStore.writeShort(offset, i16); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeInt(int i) { long offset = writeOffsetPositionMoved(4); bytesStore.writeInt(offset, i); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> prewriteInt(int i) { long offset = prewriteOffsetPositionMoved(4); bytesStore.writeInt(offset, i); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeLong(long i64) { long offset = writeOffsetPositionMoved(8); bytesStore.writeLong(offset, i64); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> prewriteLong(long i64) { long offset = prewriteOffsetPositionMoved(8); bytesStore.writeLong(offset, i64); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeFloat(float f) { long offset = writeOffsetPositionMoved(4); bytesStore.writeFloat(offset, f); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeDouble(double d) { long offset = writeOffsetPositionMoved(8); bytesStore.writeDouble(offset, d); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> write(byte[] bytes, int offset, int length) { long offsetInRDO = writeOffsetPositionMoved(length); bytesStore.write(offsetInRDO, bytes, offset, length); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> prewrite(byte[] bytes) { long offsetInRDO = prewriteOffsetPositionMoved(bytes.length); bytesStore.write(offsetInRDO, bytes); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> write(@NotNull ByteBuffer buffer) { bytesStore.write(writePosition, buffer, buffer.position(), buffer.limit()); writePosition += buffer.remaining(); assert writePosition <= writeLimit(); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeOrderedInt(int i) { long offset = writeOffsetPositionMoved(4); bytesStore.writeOrderedInt(offset, i); return this; } @NotNull @Override @ForceInline public Bytes<Underlying> writeOrderedLong(long i) { long offset = writeOffsetPositionMoved(8); bytesStore.writeOrderedLong(offset, i); return this; } @Override public long address(long offset) { return bytesStore.address(offset); } @Override public int hashCode() { throw new UnsupportedOperationException("todo"); } @Override public boolean equals(Object obj) { if (!(obj instanceof Bytes)) return false; Bytes b2 = (Bytes) obj; long remaining = readRemaining(); return b2.readRemaining() == remaining && equalsBytes(b2, remaining); } public boolean equalsBytes(@NotNull Bytes b2, long remaining) { long i = 0; try { for (; i < remaining - 7; i++) if (readLong(readPosition() + i) != b2.readLong(b2.readPosition() + i)) return false; for (; i < remaining; i++) if (readByte(readPosition() + i) != b2.readByte(b2.readPosition() + i)) return false; } catch (BufferUnderflowException | IORuntimeException e) { throw Jvm.rethrow(e); } return true; } @NotNull @Override public String toString() { return BytesInternal.toString(this); } @Override @ForceInline public void nativeRead(long address, long size) { bytesStore.nativeRead(readPosition(), address, size); readSkip(size); } @Override @ForceInline public void nativeWrite(long address, long size) { bytesStore.nativeWrite(address, writePosition(), size); writeSkip(size); } @Override @ForceInline public void nativeRead(long position, long address, long size) { bytesStore.nativeRead(position, address, size); } @Override @ForceInline public void nativeWrite(long address, long position, long size) { bytesStore.nativeWrite(address, position, size); } @Nullable @Override public BytesStore bytesStore() { return bytesStore; } public int byteCheckSum() throws IORuntimeException { if (readLimit() >= Integer.MAX_VALUE || start() != 0) throw new AssertionError(); byte b = 0; NativeBytesStore bytesStore = (NativeBytesStore) bytesStore(); Memory memory = bytesStore.memory; assert memory != null; for (int i = (int) readPosition(), lim = (int) readLimit(); i < lim; i++) { b += memory.readByte(bytesStore.address + i); } return b & 0xFF; } @NotNull public Bytes<Underlying> append8bit(@NotNull CharSequence cs) throws BufferOverflowException, BufferUnderflowException, IORuntimeException { if (cs instanceof BytesStore) { return write((BytesStore) cs); } int length = cs.length(); long offset = writeOffsetPositionMoved(length); long address = bytesStore.address(offset); Memory memory = bytesStore.memory; assert memory != null; for (int i = 0; i < length; i++) { char c = cs.charAt(i); if (c > 255) c = '?'; memory.writeByte(address + i, (byte) c); } return this; } @Override public int lastDecimalPlaces() { return lastDecimalPlaces; } @Override public void lastDecimalPlaces(int lastDecimalPlaces) { this.lastDecimalPlaces = Math.max(0, lastDecimalPlaces); } }