// Copyright © 2011-2013, Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0
package fi.jumi.core.ipc.buffer;
import javax.annotation.concurrent.NotThreadSafe;
import java.nio.*;
@NotThreadSafe
public class IpcBuffer {
private final ByteBufferSequence buffers;
private int position = 0;
private Segment current;
public IpcBuffer(ByteBufferSequence buffers) {
this.buffers = buffers;
current = new Segment();
}
public int position() {
return position;
}
public IpcBuffer position(int newPosition) {
if (newPosition < 0) {
throw new IllegalArgumentException();
}
position = newPosition;
return this;
}
private Segment segmentContaining(int index) {
if (index < 0) {
throw new IndexOutOfBoundsException();
}
while (index < current.startInclusive) {
current = current.prev();
}
while (index >= current.endExclusive) {
current = current.next();
}
return current;
}
// absolute get
public byte getByte(int index) {
Segment segment = segmentContaining(index);
return segment.buffer.get(segment.relativize(index));
}
public short getShort(int index) {
Segment segment = segmentContaining(index);
if (index + 2 <= segment.endExclusive) {
return segment.buffer.getShort(segment.relativize(index));
} else {
return (short) ((getByte(index) & 0xFF) << 8 | getByte(index + 1) & 0xFF);
}
}
public char getChar(int index) {
return (char) getShort(index);
}
public int getInt(int index) {
Segment segment = segmentContaining(index);
if (index + 4 <= segment.endExclusive) {
return segment.buffer.getInt(segment.relativize(index));
} else {
return (getShort(index) & 0xFFFF) << 16 | getShort(index + 2) & 0xFFFF;
}
}
public long getLong(int index) {
Segment segment = segmentContaining(index);
if (index + 8 <= segment.endExclusive) {
return segment.buffer.getLong(segment.relativize(index));
} else {
return (getInt(index) & 0xFFFFFFFFL) << 32 | getInt(index + 4) & 0xFFFFFFFFL;
}
}
// absolute set
public IpcBuffer setByte(int index, byte value) {
Segment segment = segmentContaining(index);
segment.buffer.put(segment.relativize(index), value);
return this;
}
public IpcBuffer setShort(int index, short value) {
Segment segment = segmentContaining(index);
if (index + 2 <= segment.endExclusive) {
segment.buffer.putShort(segment.relativize(index), value);
} else {
setByte(index, (byte) (value >>> 8));
setByte(index + 1, (byte) value);
}
return this;
}
public IpcBuffer setChar(int index, char value) {
return setShort(index, (short) value);
}
public IpcBuffer setInt(int index, int value) {
Segment segment = segmentContaining(index);
if (index + 4 <= segment.endExclusive) {
segment.buffer.putInt(segment.relativize(index), value);
} else {
setShort(index, (short) (value >>> 16));
setShort(index + 2, (short) value);
}
return this;
}
public IpcBuffer setLong(int index, long value) {
Segment segment = segmentContaining(index);
if (index + 8 <= segment.endExclusive) {
segment.buffer.putLong(segment.relativize(index), value);
} else {
setInt(index, (int) (value >>> 32));
setInt(index + 4, (int) value);
}
return this;
}
// relative read
public byte readByte() {
byte value = getByte(position);
position += 1;
return value;
}
public short readShort() {
short value = getShort(position);
position += 2;
return value;
}
public char readChar() {
return (char) readShort();
}
public int readInt() {
int value = getInt(position);
position += 4;
return value;
}
public long readLong() {
long value = getLong(position);
position += 8;
return value;
}
// relative write
public IpcBuffer writeByte(byte value) {
setByte(position, value);
position += 1;
return this;
}
public IpcBuffer writeShort(short value) {
setShort(position, value);
position += 2;
return this;
}
public IpcBuffer writeChar(char value) {
return writeShort((short) value);
}
public IpcBuffer writeInt(int value) {
setInt(position, value);
position += 4;
return this;
}
public IpcBuffer writeLong(long value) {
setLong(position, value);
position += 8;
return this;
}
@NotThreadSafe
private class Segment {
public final Segment prev;
public Segment next;
private final int segmentIndex;
public final ByteBuffer buffer;
public final int startInclusive;
public final int endExclusive;
public Segment() {
prev = null;
segmentIndex = 0;
buffer = buffers.get(segmentIndex);
startInclusive = 0;
endExclusive = buffer.capacity();
}
public Segment(Segment prev) {
this.prev = prev;
segmentIndex = prev.segmentIndex + 1;
buffer = buffers.get(segmentIndex);
startInclusive = prev.endExclusive;
endExclusive = startInclusive + buffer.capacity();
}
private int relativize(int index) {
return index - startInclusive;
}
public Segment prev() {
if (prev == null) {
throw new BufferUnderflowException();
}
return prev;
}
public Segment next() {
if (next == null) {
next = new Segment(this);
}
return next;
}
}
}