package org.limewire.util;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
/**
* Provides static methods for reading, transferring and compacting a
* {@link ByteBuffer}.
*/
public class BufferUtils {
private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
/** Retrieves an empty ByteBuffer. */
public static ByteBuffer getEmptyBuffer() {
return EMPTY_BUFFER;
}
/**
* Cleans some data from the buffer.
* Returns how much more needs to be deleted.
* <p>
* The data in buffer is expected to be in 'reading' position.
* That is, position should be at the end of the data, limit should be capacity.
*/
public static long delete(ByteBuffer buffer, long amountToDelete) {
if (buffer.position() <= amountToDelete) {
amountToDelete -= buffer.position();
buffer.clear();
} else {
// begin: [ABCDEFG* ] where * is position, ] is limit and capacity
buffer.flip();
// now : [*BCDEFG^ ] where * is position, ^ is limit, ] is capacity
buffer.position((int)amountToDelete);
// now : [ABCD*FG^ ] where * is position, ^ is limit, ] is capacity
buffer.compact();
// end : [EFG* ] where * is position, ] is limit and capacity
amountToDelete = 0;
}
return amountToDelete;
}
/**
* Transfers all data from 'bufferSrc' to 'dst', then reads as much data
* as possible from 'channelSrc' to 'dst'.
* This returns the last amount of data that could be read from the channel.
* It does NOT return the total amount of data transferred.
*
* @return the last amount of data that could be read from the channel.
* @throws IOException
*/
public static int readAll(ByteBuffer bufferSrc, ReadableByteChannel channelSrc, ByteBuffer dst) throws IOException {
transfer(bufferSrc, dst, true);
int read = 0;
while(dst.hasRemaining() && (read = channelSrc.read(dst)) > 0);
return read;
}
/**
* Transfers as much data as possible from src to dst.
* The data in 'src' will be flipped prior to transferring & then compacted.
* Returns however much data was transferred.
*
* @return The amount of data transferred
*/
public static int transfer(ByteBuffer src, ByteBuffer dst) {
return transfer(src, dst, true);
}
/**
* Transfers as much data as possible from src to dst.
* Returns how much data was transferred.
* The data in 'src' will NOT be flipped prior to transferring if needsFlip is false.
*
* @param needsFlip whether or not to flip src
* @return The amount of data transferred
*/
public static int transfer(ByteBuffer src, ByteBuffer dst, boolean needsFlip) {
int read = 0;
if (src != null) {
if (needsFlip) {
if (src.position() > 0) {
src.flip();
read = doTransfer(src, dst);
if(src.hasRemaining())
src.compact();
else
src.clear();
}
} else {
if (src.hasRemaining())
read = doTransfer(src, dst);
}
}
return read;
}
public static long transfer(ByteBuffer from, ByteBuffer [] to,
int offset, int length, boolean needsFlip) {
long read = 0;
for (int i = offset; i < offset + length ;i++)
read += transfer(from, to[i], needsFlip);
return read;
}
/**
* Transfers data from 'src' to 'dst'. This assumes that there
* is data in src and that it is non-null. This also assumes that
* 'src' is already flipped & 'dst' is ready for writing.
*
* @return The amount of data transferred
*/
private static int doTransfer(ByteBuffer src, ByteBuffer dst) {
int read = 0;
int remaining = src.remaining();
int toRemaining = dst.remaining();
if(toRemaining >= remaining) {
dst.put(src);
read += remaining;
} else {
int limit = src.limit();
int position = src.position();
src.limit(position + toRemaining);
dst.put(src);
read += toRemaining;
src.limit(limit);
}
return read;
}
/**
* Reads data from <code>src</code>, inserting it into <code>dst</code>,
* until a full line is read. Returns true if a full line is read, false if
* more data needs to be inserted into the buffer until a full line can be
* read.
*/
public static boolean readLine(ByteBuffer src, StringBuilder dst) {
int c = -1; //the character just read
while(src.hasRemaining()) {
c = src.get();
switch(c) {
// if this was a \n character, we're done.
case '\n': return true;
// if this was a \r character, ignore it.
case '\r': continue;
// if it was any other character, append it to the buffer.
default: dst.append((char)c);
}
}
return false;
}
/**
* Reads data from <code>src</code>, inserting it into <code>dst</code>.
*
* @return number of bytes read
*/
public static int transfer(ByteBuffer src, StringBuilder dst) {
int written = 0;
while (src.hasRemaining()) {
dst.append((char) src.get());
written++;
}
return written;
}
}