/* * Copyright 2016 higherfrequencytrading.com * * 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.lang.io; import net.openhft.lang.io.serialization.BytesMarshallableSerializer; import net.openhft.lang.io.serialization.JDKZObjectSerializer; import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory; import net.openhft.lang.model.constraints.NotNull; import sun.nio.ch.DirectBuffer; import java.io.EOFException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; /** * @author peter.lawrey */ public class ByteBufferBytes extends AbstractBytes implements IByteBufferBytes { private final ByteBuffer buffer; private final int start; private final int capacity; private int position; private int limit; private AtomicBoolean barrier; /** * Use the ByteBufferBytes.wrap(ByteBuffer) as DirectByteBuffer more efficient */ @Deprecated public ByteBufferBytes(ByteBuffer buffer) { this(buffer, 0, buffer.capacity()); } /** * Use the ByteBufferBytes.wrap(ByteBuffer) as DirectByteBuffer more efficient * * @param buffer the buffer to populate * @param start start of buffer * * @param capacity len of buffer */ @Deprecated public ByteBufferBytes(ByteBuffer buffer, int start, int capacity) { super(BytesMarshallableSerializer.create(new VanillaBytesMarshallerFactory(), JDKZObjectSerializer.INSTANCE), new AtomicInteger(1)); // We should set order to native, because compare-and-swap operations // end up with native ops. Bytes interfaces handles only native order. buffer.order(ByteOrder.nativeOrder()); this.buffer = buffer; this.start = position = start; this.capacity = limit = (capacity + start); } public static IByteBufferBytes wrap(ByteBuffer buffer) { if (buffer instanceof DirectBuffer) { return new DirectByteBufferBytes(buffer); } return new ByteBufferBytes(buffer.slice()); } public static IByteBufferBytes wrap(ByteBuffer buffer, int start, int capacity) { if (buffer instanceof DirectBuffer) { return new DirectByteBufferBytes(buffer, start, capacity); } return new ByteBufferBytes(buffer.slice(), start, capacity); } @Override public ByteBufferBytes slice() { return new ByteBufferBytes(buffer(), position, limit - position); } @Override public ByteBufferBytes slice(long offset, long length) { long sliceStart = position + offset; assert sliceStart >= start && sliceStart < capacity; long sliceEnd = sliceStart + length; assert sliceEnd > sliceStart && sliceEnd <= capacity; return new ByteBufferBytes(buffer(), (int) sliceStart, (int) length); } @Override public CharSequence subSequence(int start, int end) { int subStart = position + start; if (subStart < position || subStart > limit) throw new IndexOutOfBoundsException(); int subEnd = position + end; if (subEnd < subStart || subEnd > limit) throw new IndexOutOfBoundsException(); if (start == end) return ""; return new ByteBufferBytes(buffer(), subStart, end - start); } @Override public ByteBufferBytes bytes() { return new ByteBufferBytes(buffer(), start, capacity - start); } @Override public ByteBufferBytes bytes(long offset, long length) { long sliceStart = start + offset; assert sliceStart >= start && sliceStart < capacity; long sliceEnd = sliceStart + length; assert sliceEnd > sliceStart && sliceEnd <= capacity; return new ByteBufferBytes(buffer(), (int) sliceStart, (int) length); } @Override public long address() { if (buffer instanceof DirectBuffer) { long address = ((DirectBuffer) buffer).address(); if (address == 0) throw new IllegalStateException("This buffer has no address, is it empty?"); return address; } throw new IllegalStateException("A heap ByteBuffer doesn't have a fixed address"); } @Override public Bytes zeroOut() { clear(); int i = start; for (; i < capacity - 7; i++) buffer.putLong(i, 0L); for (; i < capacity; i++) buffer.put(i, (byte) 0); return this; } @Override public Bytes zeroOut(long start, long end) { if (start < 0 || end > limit()) throw new IllegalArgumentException("start: " + start + ", end: " + end); if (start >= end) return this; int i = (int) (this.start + start); int j = (int) (this.start + end); for (; i < j - 7; i++) buffer.putLong(i, 0L); for (; i < j; i++) buffer.put(i, (byte) 0); return this; } @Override public Bytes zeroOut(long start, long end, boolean ifNotZero) { // ByteBuffers are allocated in memory eagerly. return zeroOut(start, end); } public ByteBuffer buffer() { return buffer; } void readBarrier() { if (barrier == null) barrier = new AtomicBoolean(); barrier.get(); } void writeBarrier() { if (barrier == null) barrier = new AtomicBoolean(); barrier.lazySet(false); } @Override public int read(@NotNull byte[] bytes, int off, int len) { checkArrayOffs(bytes.length, off, len); long left = remaining(); if (left <= 0) return -1; int len2 = (int) Math.min(left, len); for (int i = 0; i < len2; i++) bytes[off + i] = readByte(); return len2; } @Override public byte readByte() { if (position < capacity) return buffer.get(position++); throw new IndexOutOfBoundsException(); } @Override public byte readByte(long offset) { int pos = (int) (start + offset); if (pos < capacity) return buffer.get(pos); throw new IndexOutOfBoundsException(); } @Override public void readFully(@NotNull byte[] b, int off, int len) { checkArrayOffs(b.length, off, len); long left = remaining(); if (left < len) throw new IllegalStateException(new EOFException()); for (int i = 0; i < len; i++) b[off + i] = readByte(); } @Override public void readFully(@NotNull char[] data, int off, int len) { checkArrayOffs(data.length, off, len); long left = remaining(); if (left < len * 2L) throw new IllegalStateException(new EOFException()); for (int i = 0; i < len; i++) data[off + i] = readChar(); } @Override public void readFully(long offset, byte[] bytes, int off, int len) { checkArrayOffs(bytes.length, off, len); long left = remaining(); if (left < len) throw new IllegalStateException(new EOFException()); for (int i = 0; i < len; i++) bytes[off + i] = readByte(offset + i); } @Override public short readShort() { if (position + 2 <= capacity) { short s = buffer.getShort(position); position += 2; return s; } throw new IndexOutOfBoundsException(); } @Override public short readShort(long offset) { int pos = (int) (start + offset); if (pos + 2 <= capacity) return buffer.getShort(pos); throw new IndexOutOfBoundsException(); } @Override public char readChar() { if (position + 2 <= capacity) { char ch = buffer.getChar(position); position += 2; return ch; } throw new IndexOutOfBoundsException(); } @Override public char readChar(long offset) { int pos = (int) (start + offset); if (pos + 2 <= capacity) return buffer.getChar(pos); throw new IndexOutOfBoundsException(); } @Override public int readInt() { if (position + 4 <= capacity) { int i = buffer.getInt(position); position += 4; return i; } throw new IndexOutOfBoundsException(); } @Override public int readInt(long offset) { int pos = (int) (start + offset); if (pos + 4 <= capacity) return buffer.getInt(pos); throw new IndexOutOfBoundsException(); } @Override public int readVolatileInt() { readBarrier(); return readInt(); } @Override public int readVolatileInt(long offset) { readBarrier(); return readInt(offset); } @Override public long readLong() { if (position + 8 <= capacity) { long l = buffer.getLong(position); position += 8; return l; } throw new IndexOutOfBoundsException(); } @Override public long readLong(long offset) { int pos = (int) (start + offset); if (pos + 8 <= capacity) return buffer.getLong(pos); throw new IndexOutOfBoundsException(); } @Override public long readVolatileLong() { readBarrier(); return readLong(); } @Override public long readVolatileLong(long offset) { readBarrier(); return readLong(offset); } @Override public float readFloat() { if (position + 4 <= capacity) { float f = buffer.getFloat(position); position += 4; return f; } throw new IndexOutOfBoundsException(); } @Override public float readFloat(long offset) { int pos = (int) (start + offset); if (pos + 4 <= capacity) return buffer.getFloat(pos); throw new IndexOutOfBoundsException(); } @Override public double readDouble() { if (position + 8 <= capacity) { double d = buffer.getDouble(position); position += 8; return d; } throw new IndexOutOfBoundsException(); } @Override public double readDouble(long offset) { int pos = (int) (start + offset); if (pos + 8 <= capacity) return buffer.getDouble(pos); throw new IndexOutOfBoundsException(); } @Override public void write(int b) { if (position < capacity) buffer.put(position++, (byte) b); else throw new IndexOutOfBoundsException(); } @Override public void writeByte(long offset, int b) { int pos = (int) (start + offset); if (pos < capacity) buffer.put(pos, (byte) b); else throw new IndexOutOfBoundsException(); } @Override public void writeShort(int v) { if (position + 2 <= capacity) { buffer.putShort(position, (short) v); position += 2; } else { throw new IndexOutOfBoundsException(); } } @Override public void writeShort(long offset, int v) { int pos = (int) (start + offset); if (pos + 2 <= capacity) buffer.putShort(pos, (short) v); else throw new IndexOutOfBoundsException(); } @Override public void writeChar(int v) { if (position + 2 <= capacity) { buffer.putChar(position, (char) v); position += 2; } else { throw new IndexOutOfBoundsException(); } } @Override public void writeChar(long offset, int v) { int pos = (int) (start + offset); if (pos + 2 <= capacity) buffer.putChar(pos, (char) v); else throw new IndexOutOfBoundsException(); } @Override public void writeInt(int v) { if (position + 4 <= capacity) { buffer.putInt(position, v); position += 4; } else { throw new IndexOutOfBoundsException(); } } @Override public void writeInt(long offset, int v) { int pos = (int) (start + offset); if (pos + 4 <= capacity) buffer.putInt(pos, v); else throw new IndexOutOfBoundsException(); } @Override public void writeOrderedInt(int v) { writeInt(v); writeBarrier(); } @Override public void writeOrderedInt(long offset, int v) { writeInt(offset, v); writeBarrier(); } @Override public boolean compareAndSwapInt(long offset, int expected, int x) { if (buffer instanceof DirectBuffer) return NativeBytes.UNSAFE.compareAndSwapInt(null, ((DirectBuffer) buffer).address() + offset, expected, x); return NativeBytes.UNSAFE.compareAndSwapInt(buffer.array(), NativeBytes.BYTES_OFFSET + offset, expected, x); } @Override public void writeLong(long v) { if (position + 8 <= capacity) { buffer.putLong(position, v); position += 8; } else { throw new IndexOutOfBoundsException(); } } @Override public void writeLong(long offset, long v) { int pos = (int) (start + offset); if (pos + 8 <= capacity) buffer.putLong(pos, v); else throw new IndexOutOfBoundsException(); } @Override public void writeOrderedLong(long v) { writeLong(v); writeBarrier(); } @Override public void writeOrderedLong(long offset, long v) { writeLong((int) offset, v); writeBarrier(); } @Override public boolean compareAndSwapLong(long offset, long expected, long x) { if (buffer instanceof DirectBuffer) return NativeBytes.UNSAFE.compareAndSwapLong(null, ((DirectBuffer) buffer).address() + offset, expected, x); return NativeBytes.UNSAFE.compareAndSwapLong(buffer.array(), NativeBytes.BYTES_OFFSET + offset, expected, x); } @Override public void writeFloat(float v) { if (position + 4 <= capacity) { buffer.putFloat(position, v); position += 4; } else { throw new IndexOutOfBoundsException(); } } @Override public void writeFloat(long offset, float v) { int pos = (int) (start + offset); if (pos + 4 <= capacity) buffer.putFloat(pos, v); else throw new IndexOutOfBoundsException(); } @Override public void writeDouble(double v) { if (position + 8 <= capacity) { buffer.putDouble(position, v); position += 8; } else { throw new IndexOutOfBoundsException(); } } @Override public void writeDouble(long offset, double v) { int pos = (int) (start + offset); if (pos + 8 <= capacity) buffer.putDouble(pos, v); else throw new IndexOutOfBoundsException(); } @Override public void readObject(Object object, int start, int end) { throw new UnsupportedOperationException(); } @Override public void writeObject(Object object, int start, int end) { throw new UnsupportedOperationException(); } @Override public long position() { return position - start; } @Override public ByteBufferBytes position(long position) { if (start + position > Integer.MAX_VALUE) throw new IllegalArgumentException("Position to large"); if (position < 0) throw new IllegalArgumentException("Position to small"); this.position = (int) (start + position); return this; } @Override public long capacity() { return capacity - start; } @Override public long remaining() { return limit - position; } @Override public long limit() { return limit - start; } @Override public ByteBufferBytes limit(long limit) { this.limit = (int) (start + limit); return this; } @NotNull @Override public ByteOrder byteOrder() { return buffer.order(); } @Override public void checkEndOfBuffer() throws IndexOutOfBoundsException { if (position < start || position > limit()) { throw new IndexOutOfBoundsException(); } } @Override protected void cleanup() { IOTools.clean(buffer); } @Override public Bytes load() { int pageSize = NativeBytes.UNSAFE.pageSize(); for (int offset = start; offset < capacity; offset += pageSize) buffer.get(offset); return this; } public void alignPositionAddr(int powerOf2) { position = (position + powerOf2 - 1) & ~(powerOf2 - 1); } }