package com.limegroup.gnutella.util; import java.io.IOException; import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; /** * A circular buffer - allows to read and write to and from channels and other buffers * with virtually no memory overhead. */ public class CircularByteBuffer { private final ByteBuffer in, out; private boolean lastOut = true; public CircularByteBuffer(int capacity, boolean direct) { if (direct) in = ByteBuffer.allocateDirect(capacity); else in = ByteBuffer.allocate(capacity); out = in.duplicate(); } public final int remainingIn() { int i = in.position(); int o = out.position(); if (i > o) return in.capacity() - i + o; if (i < o) return o - i; else return lastOut ? in.capacity() : 0; } public final int remainingOut() { return in.capacity() - remainingIn(); } public void put(ByteBuffer src) { if (src.remaining() > remainingIn()) throw new BufferOverflowException(); if (src.remaining() > in.remaining()) { int oldLimit = src.limit(); src.limit(src.position() + in.remaining()); in.put(src); in.rewind(); src.limit(oldLimit); } in.put(src); lastOut = false; } public void put(CircularByteBuffer src) { if (src.remainingOut() > remainingIn()) throw new BufferOverflowException(); if (in.remaining() < src.remainingOut()) { src.out.limit(in.remaining()); in.put(src.out); in.rewind(); src.out.limit(src.out.capacity()); } in.put(src.out); lastOut = false; } public byte get() { if (remainingOut() < 1) throw new BufferUnderflowException(); if (!out.hasRemaining()) out.rewind(); lastOut = true; return out.get(); } public void get(byte [] dest) { get(dest,0,dest.length); } public void get(byte [] dest, int offset, int length) { if (remainingOut() < length) throw new BufferUnderflowException(); if (out.remaining() < length) { int remaining = out.remaining(); out.get(dest, offset, remaining); offset+=remaining; length-=remaining; out.rewind(); } out.get(dest,offset,length); lastOut = true; } public void get(ByteBuffer dest) { if (remainingOut() < dest.remaining()) throw new BufferUnderflowException(); if (out.remaining() < dest.remaining()) { dest.put(out); out.rewind(); } dest.put(out); lastOut = true; } public int write(WritableByteChannel sink) throws IOException { int written = 0; int thisTime = 0; while (remainingOut() > 0) { if (!out.hasRemaining()) out.rewind(); if (in.position() > out.position()) out.limit(in.position()); int pos = out.position(); try { thisTime = sink.write(out); } finally { if (out.position() > pos) lastOut = true; } out.limit(out.capacity()); if (thisTime == 0) break; written += thisTime; } return written; } public int read(ReadableByteChannel source) throws IOException { int read = 0; int thisTime = 0; while (remainingIn() > 0){ if (!in.hasRemaining()) in.rewind(); if (out.position() > in.position()) in.limit(out.position()); int pos = in.position(); try { thisTime = source.read(in); } finally { if (in.position() > pos) lastOut = false; } in.limit(in.capacity()); if (thisTime == 0) break; read += thisTime; } return read; } }