import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class UnsafeTestOsm2 {

    public static void main(String[] args) throws Exception {
        new UnsafeTestOsm2().run();
    }

    public void run() throws Exception {
        for (int i = 0; i < 200; i++) {
            snapshot2();
        }
    }

    private void snapshot2() {
        long addrA = MyNativeBytes.UNSAFE.allocateMemory(150);
        AnotherTailer tailer = new AnotherTailer(addrA, addrA + 150);
            for (int j = 0; j < 100; j++) {
                addMessage(tailer);
            }

        long addrB = MyNativeBytes.UNSAFE.allocateMemory(150);
        YetAnotherTailer snapshotTailer1 = new YetAnotherTailer(addrB, addrB + 150);

        for (int j = 0; j < 100; j++) {
                addMessage(snapshotTailer1);
            }
    }

    /**
     * Adds the message with the given index and content to the snapshot.
     */
    public void addMessage(MyRandomDataInput bytes) {
        bytes.clear();
        if (bytes.capacity() > 150) {
            bytes.limit(150);
        }
        while (bytes.remaining() > 0) {
            if (!buffer.hasRemaining()) {
                flush();
            }
            readInlined2(bytes);
        }
    }

    public void readInlined2(MyRandomDataInput bytes) {
        int len = (int) Math.min(buffer.remaining(), bytes.remaining());

        while (len > 0) {
            buffer.put(bytes.readByte());
            len--;
        }
    }

    private final ByteBuffer buffer = ByteBuffer.allocateDirect(16384).order(ByteOrder.nativeOrder());

    public void flush() {
        buffer.clear();
    }
}

class YetAnotherTailer extends MyNativeBytes implements MyExcerptTailer {

    public YetAnotherTailer(long startAddr, long capacityAddr) {
        super(startAddr, capacityAddr);
    }

    @Override
    public boolean index(long l) {
        return false;
    }

    @Override
    public boolean nextIndex() {
        return false;
    }

    @Override
    public long index() {
        return 0;
    }

    @Override
    public long size() {
        return 0;
    }

}

class AnotherTailer extends MyNativeBytes implements MyExcerptTailer {

    public AnotherTailer(long startAddr, long capacityAddr) {
        super(startAddr, capacityAddr);
    }

    @Override
    public boolean index(long l) {
        return false;
    }

    @Override
    public boolean nextIndex() {
        return false;
    }

    @Override
    public long index() {
        return 0;
    }

    @Override
    public long size() {
        return 0;
    }
}

interface MyRandomDataInput {

    long position();

    MyBytes position(long var1) throws IllegalArgumentException;

    long limit();

    MyBytes limit(long var1);

    long capacity();

    long remaining();

    void finish() throws IndexOutOfBoundsException;

    boolean isFinished();

    MyBytes clear();

    MyBytes flip();
    byte readByte();

    byte readByte(long var1);

    int read();
}

interface MyBytes extends MyRandomDataInput {
    void clearThreadAssociation();

    long position();

    MyBytes position(long var1) throws IllegalArgumentException;

    long limit();

    MyBytes limit(long var1);

    long capacity();

    long remaining();
}

interface MyExcerptCommon extends MyBytes {

    long index();

    long size();

    void finish();
}

interface MyExcerptTailer extends MyExcerptCommon {

    boolean index(long l);

    boolean nextIndex();
}

abstract class MyAbstractBytes implements MyBytes {
    protected boolean finished;
    volatile Thread singleThread;

    public void clearThreadAssociation() {
        this.singleThread = null;
    }

    private void setThreadOrThrowException(Thread t) {
        if (this.singleThread == null) {
            this.singleThread = t;
        } else {
            String var10002 = String.valueOf(this.singleThread);
            throw new IllegalStateException("Altered by thread " + var10002 + " and " + String.valueOf(t));
        }
    }

    public void free() {
        throw new UnsupportedOperationException("Forcing a free() via Bytes is unsafe, try reserve() + release()");
    }

    protected abstract void cleanup();
    
    public int readUnsignedByte() {
        return this.readByte() & 255;
    }

    public int available() {
        return (int) Math.min(2147483647L, this.remaining());
    }

    public int read() {
        return this.remaining() > 0L ? this.readUnsignedByte() : -1;
    }

    public long skip(long n) {
        if (n < -this.position()) {
            throw new IllegalArgumentException("Skip bytes out of range, was " + n);
        } else {
            if (n > this.remaining()) {
                n = this.remaining();
            }

            this.position(this.position() + n);
            return n;
        }
    }

    public void close() {
        if (!this.isFinished()) {
            this.finish();
        }

    }

    public void finish() throws IndexOutOfBoundsException {
        if (this.remaining() < 0L) {
            this.throwOverflow();
        }

        this.finished = true;
    }

    private void throwOverflow() throws IndexOutOfBoundsException {
        long var10002 = this.capacity();
        throw new IndexOutOfBoundsException("Buffer overflow, capacity: " + var10002 + " position: " + this.position());
    }

    public boolean isFinished() {
        return this.finished;
    }

    public int length() {
        if (this.position() == 0L) {
            return (int) Math.min(this.limit(), 2147483647L);
        } else if (this.position() != this.limit() && this.limit() != this.capacity()) {
            throw new IllegalStateException();
        } else {
            return (int) Math.min(this.position(), 2147483647L);
        }
    }
}

class MyNativeBytes extends MyAbstractBytes {

    public static final Unsafe UNSAFE;
    protected static final long NO_PAGE;
    static final int BYTES_OFFSET;
    static final int CHARS_OFFSET;
    protected long startAddr;
    protected long positionAddr;
    protected long limitAddr;
    protected long capacityAddr;

    public static MyNativeBytes empty() {
        return new MyNativeBytes(NO_PAGE, NO_PAGE);
    }

    public MyNativeBytes(long startAddr, long capacityAddr) {
        this.setStartPositionAddress(startAddr, capacityAddr);
    }

    public void setStartPositionAddress(long startAddr, long capacityAddr) {
        this.setStartPositionAddress(startAddr);
        if (startAddr > capacityAddr) {
            throw new IllegalArgumentException("Missorted capacity address");
        } else {
            this.limitAddr = this.capacityAddr = capacityAddr;
            this.positionChecks(this.positionAddr);
        }
    }

    public static MyNativeBytes wrap(long address, long capacity) {
        return new MyNativeBytes(address, address + capacity);
    }

    public void setStartPositionAddress(long startAddr) {
        if ((startAddr & -16384L) == 0L) {
            throw new AssertionError("Invalid address " + Long.toHexString(startAddr));
        } else {
            this.positionAddr = this.startAddr = startAddr;
        }
    }

    public byte readByte() {
        byte aByte = UNSAFE.getByte(this.positionAddr);
        this.addPosition(1L);
        return aByte;
    }

    public byte readByte(long offset) {
        return UNSAFE.getByte(this.startAddr + offset);
    }

    private long incrementPositionAddr(long value) {
        this.positionAddr(this.positionAddr() + value);
        return this.positionAddr();
    }

    void addPosition(long delta) {
        this.positionAddr(this.positionAddr() + delta);
    }

    public long position() {
        return this.positionAddr - this.startAddr;
    }

    public MyNativeBytes position(long position) {
        if (position >= 0L && position <= this.limit()) {
            this.positionAddr(this.startAddr + position);
            return this;
        } else {
            throw new IndexOutOfBoundsException("position: " + position + " limit: " + this.limit());
        }
    }

    public MyNativeBytes lazyPosition(long position) {
        if (position >= 0L && position <= this.limit()) {
            this.positionAddr(this.startAddr + position);
            return this;
        } else {
            throw new IndexOutOfBoundsException("position: " + position + " limit: " + this.limit());
        }
    }

    public long capacity() {
        return this.capacityAddr - this.startAddr;
    }

    public long remaining() {
        return this.limitAddr - this.positionAddr;
    }

    @Override
    public MyBytes clear() {
        this.positionAddr = this.startAddr;
        this.limitAddr = this.capacityAddr;
        return this;
    }

    @Override
    public MyBytes flip() {
        return this;
    }

    public long limit() {
        return this.limitAddr - this.startAddr;
    }

    public MyNativeBytes limit(long limit) {
        if (limit >= 0L && limit <= this.capacity()) {
            this.limitAddr = this.startAddr + limit;
            return this;
        } else {
            throw new IllegalArgumentException("limit: " + limit + " capacity: " + this.capacity());
        }
    }

    protected void cleanup() {
    }

    public void alignPositionAddr(int powerOf2) {
        long value = this.positionAddr + (long) powerOf2 - 1L & (long) (~(powerOf2 - 1));
        this.positionAddr(value);
    }

    public void positionAddr(long positionAddr) {
        this.positionChecks(positionAddr);
        this.positionAddr = positionAddr;
    }

    void positionChecks(long positionAddr) {
        assert this.actualPositionChecks(positionAddr);

    }

    boolean actualPositionChecks(long positionAddr) {
        long var10002;
        if (positionAddr < this.startAddr) {
            var10002 = this.startAddr - positionAddr;
            throw new IndexOutOfBoundsException("position before the start by " + var10002 + " bytes");
        } else if (positionAddr > this.limitAddr) {
            var10002 = positionAddr - this.limitAddr;
            throw new IndexOutOfBoundsException("position after the limit by " + var10002 + " bytes");
        } else {
            return true;
        }
    }

    void offsetChecks(long offset, long len) {
        assert this.actualOffsetChecks(offset, len);

    }

    boolean actualOffsetChecks(long offset, long len) {
        if (offset >= 0L && offset + len <= this.capacity()) {
            return true;
        } else {
            throw new IndexOutOfBoundsException("offset out of bounds: " + offset + ", len: " + len + ", capacity: " + this.capacity());
        }
    }

    public long positionAddr() {
        return this.positionAddr;
    }

    static {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            UNSAFE = (Unsafe) theUnsafe.get((Object) null);
            BYTES_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
            CHARS_OFFSET = UNSAFE.arrayBaseOffset(char[].class);
        } catch (Exception var2) {
            Exception e = var2;
            throw new AssertionError(e);
        }

        NO_PAGE = UNSAFE.allocateMemory((long) UNSAFE.pageSize());
    }
}

